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.
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, runpara 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
Spend windows
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.Request consent
Request consent
A user who has not consented sees the consent screen in the Portal before the review of a matching transaction. Approve grants standing access to future matching requests, while the current request still goes through review. Manually approve each transaction instead keeps every matching request in review.Test both choices, and test Revoke permission on the standalone consent page. Consent belongs to one policy version, so after you save a new version, each user consents again.
Approval requirements
Approval requirements
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.Facts Para cannot read
Facts Para cannot read
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.- REST API
- Web and React Native SDKs
A denied request returns HTTP 403 with code A request that needs approval returns HTTP 403 with code
POLICY_DENIED. Its deniedBy entry names the policy and rule that decided, when a rule matched, and a reason.POLICY_REVIEW_REQUIRED, plus approvalCaseId, approvalCaseStatus and reason. Once the case completes, resend the identical request with the same Idempotency-Key.reason tells you why the request was denied:
A denial from an older scope-based policy can omit
deniedBy.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.