Skip to main content
Test on a device or simulator with real user accounts. Transaction review, including the consent screen it can show, opens through the handler you pass to setTransactionReviewHandler. Test every rule on a Development key before you ask Para to publish it to production. This page gives you a test plan and shows where to read each result.
Production review starts from the policy that is active on your Development key, so test the version you intend to ship. Changing beta later never updates production on its own.

Set up a test environment

Policies belong to one API key, and a Development key signs against Para’s beta environment. A few choices up front make results easier to read:
  • Use a Development key, and activate the policy on the same key your test app or backend uses. For REST, that is a beta secret key against https://api.beta.getpara.com.
  • Sign on testnets. Sepolia, Solana Devnet, Stellar Testnet and Sui Testnet keep test funds separate from real ones.
  • Prepare one account per role. Requests need a user who consents. Approval requirements need approver accounts that are separate from the account that starts the request, with roles assigned from your backend.
A selector that names a whole wallet type, with no chainId, covers testnets and mainnets alike, so the rules you test are the rules you ship. Values that differ by network, such as token contracts, still need mainnet equivalents for production.

Validate before you activate

Validation catches malformed policies before they reach signing. In the Developer Portal, Review validates the policy and lists overlapping active policies. With the CLI, run para keys permissions validate, which exits with code 10 when the policy is invalid. A policy can pass validation and still not do what you meant. Read the normalized policy on the Review step, or in Normalized policy JSON after saving, and check the overlap list: every overlapping policy applies, and a block in any overlapping Guardrail wins.
When Para refuses a CLI request with HTTP 403, the CLI usually prints a generic API_FORBIDDEN message in place of Para’s. Start by checking that your developer account can manage the organization, project and API key.

Build a test matrix

Write down the result you expect for each case before you send it. Most rows need one request that should pass and one that should not.
Spend windows are fixed buckets aligned to the Unix epoch in UTC, not rolling windows. A 1d or 24h window resets at 00:00 UTC, and a one-week window resets at 00:00 UTC on Thursday.Para compares the spend already recorded in the bucket plus the current request against the limit. Spend counts only when the request is allowed to sign, so a denied request uses no budget, and spend from before the policy was active does not count. Budgets belong to the wallet, network, asset and window, so two policies that meter the same thing share one budget.
Use a separate account for each approver. The account that started a request cannot approve its case unless the requirement collects transaction signatures, and an approver counted in one stage cannot count again in a later stage.One rejection from an eligible approver ends the case as rejected. For amount bands, send an amount just below and exactly at each minimumAmount: a stage applies once the amount reaches its threshold, and higher amounts add stages rather than replacing them. A completed approval covers one request and is used once.
Some facts depend on decoding the request, such as a contract’s method and arguments. When a rule reads a fact that is missing for a request, the rule cannot decide, and Para denies the request unless another matching rule with an equal or stronger effect decides it. Test with the exact requests your app produces.

Read the results

Each signing call tells you which way the policy decided. Denials name the deciding rule when one matched, and requests that need approval return a case you can follow.
A denied request returns HTTP 403 with code POLICY_DENIED. Its deniedBy entry names the policy and rule that decided, when a rule matched, and a reason.
A request that needs approval returns HTTP 403 with code POLICY_REVIEW_REQUIRED, plus approvalCaseId, approvalCaseStatus and reason. Once the case completes, resend the identical request with the same Idempotency-Key.
The reason tells you why the request was denied:
A denial from an older scope-based policy can omit deniedBy.
To follow an approval, read its case. status is pending, completed, rejected, expired, invalidated or cancelled, and an ended case carries a terminalReason. On SDKs, an approver reads the case with getAccessibleApprovalCase and the requester with getInitiatedApprovalCase. Your backend reads it with getPartnerApprovalCase. To check spend windows, read the policy’s usage meters. Each meter reports limitBaseUnits, usedBaseUnits and resetAt. An allow limit written with lt or lte also reports remainingBaseUnits.

Fix and test again

Edit the policy, then rerun the rows it affects. Saving changes to an active policy replaces it with the next version, and consent given for the previous version does not carry over. If the change would invalidate open approval cases, the Developer Portal and the CLI apply it only after you confirm. Keep testing until every row in your matrix gives the result you wrote down.