How Policy Evaluation Works
The policy engine evaluates rules in a strict, ordered sequence. A request fails the moment it trips any rule — subsequent checks are not run. Here is the full order:1
Action check
Is
TRANSFER included in your session’s allowedActions list? If not, the request is rejected with ACTION_NOT_ALLOWED. This is the first gate — if the action type itself is blocked, nothing else matters.2
Asset check
Is the requested asset (e.g.,
USDC, XLM) in your allowedAssets list? Asset matching is case-insensitive. If the asset is not on the list, the request fails with ASSET_NOT_ALLOWED.3
Recipient check
If your policy defines an
allowedRecipientIds list, the resolved recipient must appear in it. If the list is empty, any resolved recipient is permitted. A mismatch returns RECIPIENT_NOT_ALLOWED.4
Per-transaction limit
Is the requested amount greater than
perTransactionMax? If so, the request fails with PER_TRANSACTION_LIMIT_EXCEEDED. This check happens before budget checks — a single oversized transfer is rejected even if your daily budget has plenty of headroom.5
Daily budget
Would this transfer push
dailySpent + amount past your dailyBudget? If yes, the request fails with DAILY_BUDGET_EXCEEDED. The engine uses the cumulative spend recorded for the current 24-hour window.6
Monthly budget (optional)
If you have configured a
monthlyBudget, the engine checks whether monthlySpent + amount would exceed it. Failure returns MONTHLY_BUDGET_EXCEEDED. If no monthly budget is set, this check is skipped.7
Approval decision
All hard rules passed. Now the engine compares the amount against your
manualApprovalThreshold. If amount > manualApprovalThreshold, the transaction is marked PENDING_APPROVAL and you receive a prompt in the dashboard. Otherwise it is marked AUTO_EXECUTE and proceeds immediately.The Three Execution Outcomes
Every request exits the policy engine with exactly one of three decisions:Pending approvals expire after exactly 2 minutes. If you don’t act in time, the transaction status becomes
EXPIRED — not FAILED. No funds move and no transaction is submitted to Stellar. The AI will ask you to resubmit the request.Policy Is Frozen at Session Creation
Your policy configuration is captured as an immutable snapshot at the moment you create an AI session. If you update your policy settings in the dashboard after a session is already active, those changes apply to new sessions only. The running session continues to use the policy snapshot it was created with. This is intentional. It means:- Active AI sessions behave predictably — their rules don’t shift mid-session
- You can update global policy defaults without accidentally altering a session you’ve already reviewed and authorized
- The policy snapshot stored with a session is your audit record of what rules were in effect for every transaction that session executed
To apply updated policy rules to an existing AI client, revoke its current session and create a new one. The new session will pick up your latest policy configuration.
Policy Error Reference
ACTION_NOT_ALLOWED
ACTION_NOT_ALLOWED
Trigger: The requested action (e.g.,
TRANSFER) is not included in the session’s allowedActions list.Example: You created a read-only session with only CHECK_BALANCE and VIEW_TRANSACTION_HISTORY allowed, but the AI attempted to call transfer().Resolution: Create a new session with TRANSFER included in allowed actions, or use an AI session that has payment permissions.ASSET_NOT_ALLOWED
ASSET_NOT_ALLOWED
Trigger: The asset specified in the transfer request (e.g.,
XLM) is not in the session’s allowedAssets list.Example: Your session only permits USDC transfers, but the AI tried to send XLM.Resolution: Add the asset to your session’s allowed assets list, or create a new session that includes it.RECIPIENT_NOT_ALLOWED
RECIPIENT_NOT_ALLOWED
Trigger: Your policy has a non-empty
allowedRecipientIds list, and the resolved recipient is not on it.Example: You restricted an AI session to only pay verified vendors in your contacts list, but the AI tried to send funds to an address not in that list.Resolution: Add the recipient to your Pay3 contacts and include their contact ID in the session’s allowed recipients, or remove the recipient restriction if you want open payments.PER_TRANSACTION_LIMIT_EXCEEDED
PER_TRANSACTION_LIMIT_EXCEEDED
Trigger: The requested transfer amount is greater than
perTransactionMax for the session.Example: Your session has a $20 per-transaction cap, and the AI tried to send $50 in a single transfer.Resolution: Amounts that exceed perTransactionMax are always rejected — this is a hard limit that cannot be overridden by manual approval. To allow larger single payments, create a new session (or policy) with a higher perTransactionMax. For the current request, you can ask your AI to split the payment into multiple smaller transfers that each stay within the limit.DAILY_BUDGET_EXCEEDED
DAILY_BUDGET_EXCEEDED
Trigger: The cumulative spend for the current 24-hour window plus the new amount would exceed
dailyBudget.Example: Your daily budget is $100 USDC, you've already spent $90 today, and the AI tried to send $25 more.Resolution: Wait for the daily window to reset, or create a new session with a higher daily budget if your use case requires it.MONTHLY_BUDGET_EXCEEDED
MONTHLY_BUDGET_EXCEEDED
Trigger: The cumulative spend for the current calendar month plus the new amount would exceed
monthlyBudget. Only relevant if you have configured a monthly budget.Example: Your monthly cap is $500 USDC, you've spent $480 this month, and the AI tried to send $30.Resolution: Wait for the monthly window to reset, or increase your monthly budget limit if you need more capacity.