Skip to main content
A rule’s condition tests facts: values Para reads from the signing request, from the wallet’s record, or from the spend it tracks. This page lists every fact a policy can read for each wallet type, with its value type, operators, actions and value format. Policy JSON schema covers the document around the conditions.
The CLI prints the same catalog for your API key: run para keys permissions catalog --json.
Validate conditions against a beta API key, shown as Development in the Developer Portal. Policies reach production only through a production review: select Request production review on your production key.

Condition kinds

Leaf conditions read one fact. Group conditions combine other conditions, so one rule can express AND, OR and NOT logic.
A leaf can also be indeterminate, when the fact is missing from the request, or not applicable, when a metered fact does not apply. Condition outcomes shows how groups combine them.

Operators

Each fact supports the operators its value type allows. The fact tables list them per fact.
ne does not match a missing fact. A rule that reads a fact the request does not provide is indeterminate, whatever the operator.

Values and encodings

Each value must have the fact’s type. A wrong type fails validation with value_type_mismatch. Amounts are base units: wei and token units on EVM (1 USDC is "1000000" with 6 decimals), lamports on Solana, the chain’s base denom on Cosmos (such as uatom), stroops on Stellar, and MIST on Sui. Token amounts use the token’s smallest unit as well: the mint’s decimals on Solana, the denom’s integer amount on Cosmos, and 7 decimals for every Stellar asset, so 1 USDC on Stellar is "10000000".

Identifiers by wallet type

Write addresses and assets in the canonical form of the policy’s wallet type, exactly as shown.
Validation checks request.to, request.target and request.token.asset on Solana, Cosmos and Stellar, and request.to and request.target on Sui, and rejects other forms with chain_identity_invalid. It does not check identity formats for request.value.asset on any wallet type or for EVM request.to, request.target, request.token.asset, request.safe.to, request.safe.value.asset and request.safe.token.asset. Safe amount facts still require digit strings and reject other values with value_type_mismatch.A malformed identity string can validate and still match a ne condition against a present valid value.
For a Stellar network other than testnet or mainnet, <hash> is the SHA-256 hash of the network passphrase in lowercase hex. On EVM, eq, ne and in on request.token.asset and request.safe.token.asset compare the contract-address part of eip155:<chainId>:token:<contract> without regard to case. The prefix and chain ID must still match exactly.

Limits

Condition groups and leaves share these depth and count limits; metered facts also constrain window declarations.

Reading the fact tables

The tables below list every fact a policy can read for each wallet type, outside Metered facts. The Actions column lists the rule actions that can read the fact. Every action in a rule’s actions must appear there, or validation fails with fact_action_unsupported. all means every action of the wallet type, including raw_signature, and - means no rule can read the fact. The Present column says whether every request of those actions carries the fact. “May be missing” means Para provides the fact only when it can read it from the request, such as a recipient decoded from calldata. A rule that reads a missing fact is indeterminate and can deny the request; see Facts Para cannot read. "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. A rule that names raw_signature can read only the para.wallet.* facts.

EVM

Choose facts by the signing action: EVM transaction and message actions expose different fields. What each fact holds on EVM: The Para client and server SDKs, including their viem, ethers and wagmi integrations, sign EIP-712 typed data and EIP-7702 authorizations as a message over their hash, so Para evaluates those requests as sign_message. The sign_typed_data and sign_authorization actions, and the facts Para reads from typed data and authorizations, such as request.typedData.primaryType, apply only to the REST API’s sign-typed-data and sign-authorization routes, which the REST SDK calls. Para decodes ERC-20 and NFT calls itself. For other contract calls it uses the contract’s verified ABI from a block explorer. Calls it cannot decode have the classification undecodable_contract_call and no request.method, request.args[n], request.to or request.contract.address. A native send that carries any calldata is call_contract, not transfer_native. request.value.baseUnits is present on every EVM transaction, "0" when it attaches no value, so a native amount cap can name both actions.
request.raw.opaque is in the catalog, but no rule can read it. Para sets it only on raw signatures, and raw_signature rules read only para.wallet.* facts, so validation rejects it with fact_action_unsupported on every action.
Spending limits use the metered facts.

Safe payments

The request.safe.* facts describe the payment inside a Safe transaction. Para reads them from two kinds of request:
  • An execTransaction call, with the action execute_safe_transaction.
  • A SafeTx EIP-712 message sent to REST sign-typed-data, with the action sign_typed_data. Its domain must hold only chainId and verifyingContract, its message must hold exactly the SafeTx fields, and its types must declare only SafeTx, optionally with an EIP712Domain of those two fields; otherwise Para treats it as opaque. A SafeTx signed through the Para client and server SDKs arrives as sign_message and has no Safe facts.
Safe facts and Safe spend limits apply only to these two requests. A SafeTx that an owner wallet co-signs through the SDKs is a sign_message, and an owner wallet’s direct call to the Safe, such as approveHash, is a call_contract, so no rule on Safe facts sees either. Once the Safe has enough approvals, anyone can execute the payment outside Para. To block direct calls, see Direct calls to the Safe.
Para reads a Safe transaction as a payment only when all of these hold:
  • operation is 0, a Call. A DelegateCall is never a payment.
  • gasPrice is 0, so the Safe pays no gas refund.
  • The inner to is not the Safe itself.
  • The inner call is a plain native transfer, or one ERC-20 transfer or transferFrom that sends no native value.
A readable payment provides request.safe.to and the two facts of its payment family. The facts of the other family are not applicable. A condition on a fact of the other family is not applicable and never matches. A rule that reads only native payment facts therefore does not apply to an ERC-20 payment, or the reverse, and the request goes on to your other rules or the default.
In a rule that reads both families, Para treats the absent family as missing instead. Its conditions on the absent family are indeterminate, so the rule is indeterminate unless another condition settles the group, and an indeterminate rule can deny the payment with missing-fact. For example, a deny rule that matches any native amount above 1 ETH or token amount above 1,000 USDC denies a payment of 0.1 ETH. Write a separate rule for each family.
A zero-amount payment is still readable. Its amount conditions are evaluated as usual, so "op": "gt", "value": "0" does not match it, and the other family stays not applicable. Every other Safe transaction is opaque, and all of its request.safe.* facts are missing. Opaque transactions include:
  • DelegateCalls and MultiSend batches.
  • SafeTx typed data in another shape, such as a domain with extra fields.
  • Transactions with a nonzero gasPrice.
  • Calls to the Safe itself, such as owner and threshold changes.
  • Nested Safe calls, such as execTransaction or approveHash on another Safe.
  • Contract calls other than one ERC-20 transfer or transferFrom, and calldata Para cannot decode.
  • An ERC-20 transfer that also sends native value.
An owner wallet’s direct call to the Safe is not a Safe transaction and has no Safe facts. See Direct calls to the Safe. A rule that reads a request.safe.* fact is indeterminate on an opaque transaction and can deny it with missing-fact. An approval does not help: once another rule’s approval case completes, the approved rule no longer counts, so the indeterminate rule can still deny the signing request. Three facts are the same for a readable payment and an opaque transaction: request.classification, request.method and request.typedData.primaryType. On execute_safe_transaction, the decoded execTransaction arguments separate DelegateCalls and gas refunds: request.args[3] is the operation and request.args[6] is the gasPrice, and both are "0" in every readable payment. No fact separates the other opaque forms, or any opaque SafeTx typed data.
Typed data other than a SafeTx, such as an ERC-2612 Permit, has no Safe facts either. A sign_typed_data rule that reads them is indeterminate on that typed data whatever its effect, so Para denies the request with missing-fact unless another matching rule has the same or a stronger effect. This holds for require_approval and allow rules too, so the request is denied instead of going to review.
To keep a sign_typed_data rule that reads Safe facts from denying other typed data, add a request.typedData.primaryType test to an all group:
request.safe.to names the inner recipient whatever native value the outer call carries. On execute_safe_transaction, constrain request.value.baseUnits as well. sign_typed_data has no request.value.* facts, so a rule that reads them cannot name both actions. This Guardrail covers app-owned wallets that execute Safe transactions on Base. The first rule denies Safe payments to anyone except two recipients, and because it reads request.safe.to, it also denies every opaque execTransaction. It does not cover direct calls to the Safe; see Direct calls to the Safe. The second rule denies a USDC payment that would take the Safe past 50,000 USDC in the current day. It counts only readable payments, so on its own it would not stop a MultiSend batch, a DelegateCall or a gas refund, and the first rule denies those. A payment that passes both rules falls to the Guardrail default, and the wallet signs it.
safe-payouts.json

Direct calls to the Safe

An owner wallet that calls the Safe directly, such as approveHash(safeTxHash) to approve a payment on chain, sends a call_contract with no Safe facts, so Safe rules and Safe spend limits never see it. A rule that reads Safe facts cannot name call_contract; validation fails with fact_action_unsupported. To block these calls, deny every call_contract whose request.target is the Safe:
Calls to other contracts and token transfers still sign, and execTransaction stays under your Safe rules. Use require_approval instead of deny to hold these calls for review. request.target is always present and compared without case, so the rule works whether or not Para decodes the call. Do not test request.method instead: it is missing when Para cannot decode the call, so that rule would deny every contract call Para cannot decode.

Solana

Solana’s catalog separates native and token transfer facts from program targets. On Solana, Cosmos, Stellar and Sui, request.actionFamilies lists the kinds of action a transaction contains. request.to and the amount facts are present only when the transaction contains exactly one action; Cosmos retains request.target when MsgExecuteContract is the transaction’s only message, even if attached funds add transfer actions. Solana compute budget instructions do not count as an action. Creating the recipient’s associated token account does not count either when the transaction’s only other instruction is a TransferChecked that follows it and sends that token to the new account. Every other instruction counts, such as a Memo or wrapping SOL, so a rule that reads a recipient or amount can deny a transfer that includes one.
  • request.to is the recipient of a SOL transfer. For a token transfer it is the destination token account. A wallet address in your condition also matches that wallet’s associated token account for the transfer’s mint, for SPL Token and Token-2022. Another token account the wallet owns does not match.
  • request.target is the program the instruction calls.
  • request.token.asset names the mint, as solana:token:<mint>. It does not identify the cluster, so select the network in the policy’s chainId.
  • Para reads a token transfer’s recipient, amount and mint only from a TransferChecked instruction. A plain Transfer or TransferCheckedWithFee has none of these facts, and token spending limits do not count it.
solana-usdc-payouts.json

Cosmos

Cosmos facts distinguish bank sends from contract calls. The notes below describe which requests provide recipient, target and amount fields.
  • request.to is the recipient of a bank send.
  • request.target is the contract a single MsgExecuteContract calls. Transfers have none.
  • A bank send is a native transfer only in the chain’s native denom, on cosmoshub-4, noble-1, osmosis-1, celestia, dydx-mainnet-1 and injective-1. Any other denom, or any bank send on another chain, is a token transfer.
  • As on Solana, recipients, targets and amounts are present only for a transaction with exactly one message.

Stellar

Stellar facts cover payments and contract calls; the notes below explain operation-specific availability.
  • request.to is the destination account of a payment.
  • request.target is the contract a Soroban invokeContract call targets.
  • request.token.asset and request.value.asset include the network, so a condition written for stellar:testnet does not match on mainnet.
  • Recipients, targets and amounts are present only for a transaction with exactly one operation.
  • createAccount is a transfer_native. A path payment carries transfer_native when any of its assets is XLM and transfer_tokens when any is another asset, with no recipient or amount facts, so a rule that reads an amount is indeterminate on it and spending limits do not count it.

Sui

Sui exposes native transfer and Move call facts. The notes below describe transfer families and the limits of token authoring.
  • request.to is the recipient of a SUI transfer.
  • request.target is the package a Move call targets.
  • Sui has no transfer_tokens action, authorable token facts or token spending limits. Only a rule that names "all" covers Sui token transfers. With trusted coin evidence, a readable SplitCoins followed by TransferObjects can report token_transfer in request.actionFamilies; unrecognized transfers report other.
  • Recipients, targets and amounts are present only for a transaction with exactly one action, such as a single transfer.

Classification values

request.classification accepts these 22 values on every wallet type. Values that a wallet type never produces never match, and a value outside this list fails validation with value_not_allowed_for_fact. Only a rule with "actions": "all" can read the classification of a raw signature, evm.raw_signature or raw.opaque_message. A rule that names raw_signature reads only para.wallet.* facts.

Metered facts

Metered facts read the spend Para tracks for a wallet over a time window. Use them only in aggregate conditions; a compare condition on a metered fact fails with metered_fact_window_required.
The Actions column covers all wallet types. On EVM, wallet.spend.native is available to all seven listed actions, because any EVM transaction can send native value. On Solana, Cosmos, Stellar and Sui it is available only to transfer_native. wallet.spend.token does not apply to Sui, which has no token transfer action. safe.spend.native and safe.spend.erc20 count only Safe payments; see Safe spend.

Windows

window is a positive integer followed by a unit: ms, m, h or d, such as "30m", "24h" or "7d". There is no week or month unit. The Developer Portal form writes windows in hours, "24h" for a day and "168h" for a week, and the examples on these pages follow it. Windows are fixed buckets aligned to 1970-01-01 00:00 UTC, not rolling periods. A 24h window resets at 00:00 UTC every day, and a 168h window resets every Thursday at 00:00 UTC. A policy can use only one window length for each metered fact. "24h" and "1d" count as the same length, but "24h" and "168h" on the same fact fail with metered_fact_window_conflict. To enforce both a daily and a weekly limit, put them in separate policies.

What the limit compares

Para compares the spend already recorded in the current window, plus spend reserved by requests still in progress, plus the current request’s amount, against value. A rule with "op": "lte", "value": "1000000" therefore matches while the total after this request stays at or below 1,000,000.

What counts

Metering counts the transfer forms below, which can be narrower than a rule’s action coverage.
On EVM, a token spending limit therefore does not cap transferFrom calls or approvals. A per-transaction rule on request.token.value.baseUnits covers transferFrom, and a rule that names approve_tokens can limit approvals.A spending limit alone does not deny the Solana and Stellar transfers it does not count. To stop them, add a deny rule on the same action that reads the amount, such as request.token.value.baseUnits. It is indeterminate on those transfers, so Para denies them.
On execute_safe_transaction, wallet.spend.native counts only the native value the signing wallet attaches to the outer call. The payment the Safe makes counts toward Safe spend instead. Gas and network fees never count. On Solana and Sui, metering needs a network proven from the signed transaction; if configured proof endpoints prove no network, Para denies the request with network-unverifiable. On Cosmos, native spend counts only on the chains listed in Cosmos. When a request has nothing to count for a condition, such as a zero-value transaction or a transfer of another token, the condition is not applicable and the rule does not match through it. Readable Solana native transfers and TransferChecked token transfers are summed by asset for metering, even when the transaction has no single recipient or amount fact. A meter whose applicable amount cannot be resolved is indeterminate.

Budgets

Each budget is kept per wallet, network, asset and window length.
  • A token limit with asset counts only that token. An EVM token limit without asset keeps a separate budget for each token.
  • wallet.spend.erc20 and wallet.spend.token share the same budget on EVM.
  • Budgets are shared across your policies and rules. Two rules that meter the same wallet, network, asset and window read the same spend.
  • A selector with several networks does not pool them. A daily limit on Ethereum and Base allows that amount on each network.
  • Safe spend budgets are kept per Safe instead of per wallet. See Safe spend.

When spend is recorded

Para reserves the request’s amount before signing and records the spend when the signing operation succeeds. Para can release the reservation when it knows signing did not proceed. If a dispatched call fails while the signer outcome is uncertain, the amount can remain reserved. A request adds to a budget only when a rule that meters that budget covers it: the rule that decided the request, a matching allow rule, or a deny or require_approval rule whose other conditions match the request. A payment that falls outside a limit rule’s other conditions, such as a transfer to a recipient the rule does not name, does not count toward that limit. Spend is reserved only for requests Para allows. A request approved through require_approval can take a budget past its limit. So can a request that went past an allow rule’s limit and was then approved, for example by the user in the review pop-up. It still counts toward that limit when the rule’s other conditions match it. If another request uses up the budget between the decision and the reservation, Para denies the request. The denial reason is windowed-spend-limit-exceeded.

Safe spend

safe.spend.native and safe.spend.erc20 count readable Safe payments, from execTransaction calls and from SafeTx messages sent to REST sign-typed-data. Wallet limits do not count these payments.
  • Each budget is kept per API key, Safe, network, asset and window length. Your Guardrails and Requests share it: every safe.spend rule with that window length reads the same spend, whichever wallet signs.
  • Owners who sign the same Safe transaction hash share one count. An execTransaction can use a counted co-signature’s credit when the payment matches, that co-signature has not already covered another execution, and Para recovers its signer from the first signature slot. A request covered by an already-counted key or this execution credit adds nothing, so the limit does not block it.
  • safe.spend.erc20 without asset counts every ERC-20 payment, with a separate budget for each token.
  • A zero-amount native payment does not count, and neither does an ERC-20 transferFrom that pulls tokens from an address other than the Safe. The condition is not applicable to them.
Execution credit supports ECDSA signatures with v of 27 or 28, in hex buffers with at most 32 complete 65-byte slots. Only the first complete slot is inspected. A matching signature in a later slot, another signature mode or failed signer recovery receives no credit, so the execution counts separately unless its own request count key was already counted.
A Safe spend limit counts only payments Para can read. Its conditions are not applicable to MultiSend batches, DelegateCalls, calls that pay a gas refund and every other opaque Safe transaction, so when no other rule decides one, Para’s default for the wallet applies and an app-owned wallet signs it. Pair the limit with a rule that reads a request.safe.* fact, such as the first rule of safe-payouts.json, or add a request.safe.* condition to the limit rule, so that Para denies opaque transactions with missing-fact. Neither rule sees an owner wallet’s direct calls to the Safe; see Direct calls to the Safe.
Para does not check that a wallet owns the Safe. A payment counts toward the budget of the Safe it names (the execTransaction target or the SafeTx domain’s verifyingContract) whenever a safe.spend rule covers the request, so a wallet that is not an owner can use up the Safe’s budget. Scope each policy that uses safe.spend to the Safe’s owner wallets: list them with walletId in a Guardrail, or list the users who own them with userId in a Request.

Reading usage

Read current spend with getUserPolicyUsage in the client SDKs or getPartnerPolicyUsage in the REST SDK. Each meter reports the window, its reset time, and the committed, reserved and used amounts. Meters for allow rules that use lt or lte outside not and any also report the remaining amount. Each limit condition is its own meter, so when one rule has two limits on the same fact and window, the rule allows only the smaller remainingBaseUnits. See Permissions types for the response shape. Safe meters appear only when the usage request passes safeAddress. In the client SDKs, they also need one of the user’s wallets to have made a payment request that Para metered against that Safe on that network.

User-adjustable parameters

A Request can let each user set their own amount for a limit. Add a parameter to a compare or aggregate condition, and its value becomes the default.
  • Only Request policies can be personalized. A Guardrail with a parameter validates, but its default always applies.
  • A user who changes their values must consent to the policy again.
  • Para enables adjustable parameters per API key, so beta and production keys are enabled separately. Until it does, validating or saving a policy with a parameter fails with permissions_v2_extension_unavailable.
Para does not bound the values users choose. A user can set a higher limit than the default, so if you need a ceiling, add a second condition without a parameter to the same all group. A Guardrail cannot cap it, because Guardrails never govern user-owned wallets.
After you publish a new version of the policy, each user’s saved values need their confirmation. Until the user confirms, Para denies that user’s requests that the policy selects, with the reason parameter_configuration_invalid.