Prepare a legacy typed grant or a stored unsigned budget operation
Changed onWith budget or approvalDiscovery, the experimental off-by-default wallet_budget_preparation_enabled path stores a priced unsigned owner operation and the exact intent/read/derived review. It requires a fresh signed approvalDiscovery, explicitly self-funds, and returns signing.kind owner_operation (personal_sign of raw userOpHash). It creates no usable policy or hold. Budget submit needs the separate off-by-default wallet_budget_signing_enabled flag and is limited to Sepolia. GET /wallets/{address}/policies/preparations/{id} recovers the same review. Missing/stale discovery returns 409 BUDGET_DISCOVERY_REQUIRED; incomplete discovery or failed reads return 503; 64 live unsigned records per owner returns 429. Only tested token deployments may be reset automatically: v1 Sepolia USDC. Other live allowances requiring reset return 409 BUDGET_RESET_UNSUPPORTED_TOKEN with token/spender; insufficient ETH returns 409 BUDGET_FEE_INSUFFICIENT_ETH with neededWei/availableWei. A quote above wallet_budget_max_fee_wei (default 0.005 ETH) returns 409 BUDGET_FEE_CEILING_EXCEEDED. Unsupported Permit2, NFT/operator or unaudited spending targets return 409 BUDGET_ROUTE_UNSUPPORTED. A live unknown validation authority returns BUDGET_AUTHORITY_UNKNOWN; remove it in the wallet and check again. Ava's session operations are refused with BUDGET_CHANGING while the wallet/chain hold is open; scheduled runs wait without consuming an execution. A known, unexpired legacy deferred signature returns 409 BUDGET_LEGACY_GRANT_PENDING with its latest outstanding deadline. The default legacy signing window lasts up to 30 days. Revoking a permission off chain does not invalidate its signature; migration must wait until that window closes. This route offers no nonce-cancellation remedy and an owner nonce change does not cancel a session-entity nonce.
Legacy path (no budget/discovery, non-budget wallet): Allocates the policy id, validation entity, and session signer, builds the exact installValidation calldata the signature will commit to, and returns the typed data for eth_signTypedData_v4. Stores nothing: a prepare that is never submitted leaves no state behind. The echoed fields must be passed back verbatim to :submit — the gateway recomputes everything from them, so tampering only produces a signature that no longer verifies.
When add is set and the runner has a usable grant, the merged grant starts from that grant. Actions, native recipients, and allowContractRecipient only grow. delegatedNativeRecipients is carried per recipient; when both grants name a delegate for the same recipient, the gateway records the addition's delegate. The owner signs the install call; that call's ETH row is not limited to the delegate. The acknowledgement is gateway state, checked again when the gateway sends. Each token cap is what is left on that grant (the on-chain remainder once the grant is applied, otherwise the stored cap), or what enabled automations still need when that is higher, plus the addition. A token with nothing left is omitted. A router action stays. A task whose target cannot be read does not fail prepare and does not add that unknown target.
When add is set and there is no usable grant, the merged grant is the addition plus every enabled task except ids in dropTaskIds. Acknowledgements for those running recipients come from the wallet's last stored grant, including one that was revoked. The chain read still applies, and this addition's delegate wins when both name one. A task whose token or recipient cannot be read from the stored workflow (a loop over a previous step) is 409 SESSION_POLICY_TARGET_UNRESOLVED. That is not an unsized amount. Pause the automation, or name its id in dropTaskIds. An unsized token amount on this path is 400 POLICIES_BAD_PERMISSIONS. Installing a native spend cap while a running payable cannot be sized is 409 SESSION_POLICY_NATIVE_UNSIZED, including when the runner has no usable grant. The addition or a sized running payable can be what installs that cap.
Request
- Base URLhttps://api.avaprotocol.org/api/v1
- AuthHTTP bearer
Request body
Example request
{
"approvalDiscovery": {
"owner": "0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB",
"wallet": "0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB",
"chainId": 11155111,
"candidates": [
{
"token": "0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB",
"spender": "0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB"
}
],
"allowedContracts": [
"0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB"
]
},
"budget": {
"erc20": [
{
"token": "0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB",
"amount": "500000000"
}
],
"native": {
"amount": "10000000000000000"
},
"removeActions": [
{
"target": "0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB"
}
]
},
"chainId": 11155111,
"allowedActions": [
{
"target": "0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB"
}
],
"erc20SpendCap": {
"token": "0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB",
"amount": "500000000"
},
"erc20SpendCaps": [
{
"token": "0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB",
"amount": "500000000"
}
],
"nativeRecipients": [
"0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB"
],
"delegatedNativeRecipients": [
{
"recipient": "0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB",
"delegate": "0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB"
}
],
"nativeSpendCap": {
"amount": "10000000000000000"
},
"add": {
"allowedActions": [
{
"target": "0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB"
}
],
"erc20SpendCaps": [
{
"token": "0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB",
"amount": "500000000"
}
],
"nativeRecipients": [
"0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB"
],
"delegatedNativeRecipients": [
{
"recipient": "0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB",
"delegate": "0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB"
}
],
"nativeSpendCap": {
"amount": "10000000000000000"
}
}
}Response
Payload to sign, plus the allocations submit must echo.
Example response
{
"preparationId": "01JG2FE5MDVKBPHEG0PEYSDKAC",
"signing": {
"preparationId": "01JG2FE5MDVKBPHEG0PEYSDKAC",
"userOp": {
"sender": "0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB",
"factory": "0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB",
"paymaster": "0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB"
},
"userOpHash": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"entryPoint": "0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB"
},
"budget": {
"chainId": 11155111,
"policyId": "01JG2FE5MDVKBPHEG0PEYSDKAC",
"erc20": [
{
"token": "0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB"
}
],
"unmeteredPermissions": [
{
"target": "0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB",
"token": "0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB"
}
],
"retirements": [
{
"policyId": "01JG2FE5MDVKBPHEG0PEYSDKAC",
"erc20": [
{
"token": "0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB",
"amount": "500000000"
}
],
"native": {
"amount": "10000000000000000"
}
}
],
"liveApprovals": [
{
"token": "0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB",
"spender": "0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB"
}
],
"pendingChange": {
"preparationId": "01JG2FE5MDVKBPHEG0PEYSDKAC",
"policyId": "01JG2FE5MDVKBPHEG0PEYSDKAC"
}
},
"approvalResets": [
{
"token": "0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB",
"spender": "0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB"
}
],
"unmeteredPermissions": [
{
"target": "0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB",
"token": "0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB"
}
],
"policyId": "01JG2FE5MDVKBPHEG0PEYSDKAC",
"chainId": 11155111,
"sessionSigner": "0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB",
"changes": {
"keptActions": [
{
"target": "0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB"
}
],
"addedActions": [
{
"target": "0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB"
}
],
"removedActions": [
{
"target": "0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB"
}
],
"capChanges": [
{
"token": "0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB"
}
]
},
"allowedActions": [
{
"target": "0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB"
}
],
"erc20SpendCap": {
"token": "0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB",
"amount": "500000000"
},
"erc20SpendCaps": [
{
"token": "0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB",
"amount": "500000000"
}
],
"nativeRecipients": [
"0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB"
],
"delegatedNativeRecipients": [
{
"recipient": "0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB",
"delegate": "0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB"
}
],
"nativeSpendCap": {
"amount": "10000000000000000"
}
}Changes
- breaking
the response property
became optional for the status - breaking
the response property
became optional for the status - info
added the new optional request property
- info
added the new optional request property
- info
the request property
became optional - info
added the non-success response with the status
- info
added the non-success response with the status
- info
added the optional property
to the response with the status - info
added the optional property
to the response with the status - info
added the optional property
to the response with the status - info
added the optional property
to the response with the status - info
added the optional property
to the response with the status - info
added the optional property
to the response with the status - info
added the optional property
to the response with the status - info
added the optional property
to the response with the status - info
added the optional property
to the response with the status - info
added the optional property
to the response with the status - info
added the optional property
to the response with the status - info
added the optional property
to the response with the status - info
added the optional property
to the response with the status - info
added the optional property
to the response with the status - info
added the optional property
to the response with the status - info
added the optional property
to the response with the status - info
added the optional property
to the response with the status - info
added the optional property
to the response with the status - info
added the optional property
to the response with the status - info
added the optional property
to the response with the status - info
added the optional property
to the response with the status - info
added the optional property
to the response with the status - info
added the optional property
to the response with the status - info
added the optional property
to the response with the status - info
added the optional property
to the response with the status - info
added the optional property
to the response with the status - info
added the optional property
to the response with the status - info
added the optional property
to the response with the status - info
added the optional property
to the response with the status - info
added the optional property
to the response with the status - info
added the optional property
to the response with the status
- breaking
- info
added the new optional request property
/ - info
added the new optional request property
- info
added the optional property
to the response with the status - info
added the optional property
to the response with the status - info
added the optional property
to the response with the status - info
added the optional property
to the response with the status - info
added the optional property
to the response with the status - info
added the optional property
to the response with the status - info
added the optional property
to the response with the status - info
added the optional property
to the response with the status - info
added the optional property
to the response with the status - info
added the optional property
to the response with the status - info
added the optional property
to the response with the status - info
added the optional property
to the response with the status - info
added the optional property
to the response with the status - info
added the optional property
to the response with the status - info
added the optional property
to the response with the status - info
added the optional property
to the response with the status
- info