swap, limit_order, withdraw, transfer or shield. Your backend sends it with your partner API key and a grant token. The owner doesn’t sign it: the agent’s policy decides whether it may run. A limit order rests until it fills; see Limit Orders.
At a glance
- Headers:
X-API-Key,X-Grant-Tokenand anIdempotency-Keyon every execution and withdrawal preview. A swap quote or limit-order preview (dry: true) needs only the API key. - Response:
202with a status and acorrelation_id(op_…). Follow it withGET /v1/statusuntil it’s final. - Checks: grant, policy, schedule, policy revision, USD budget, the wallet provider’s rules, then the wallet. A refusal names the layer that blocked it.
- Retries: same key and same body. A new key is new work.
Run one action
Every snippet on this page assumes this setup:swap.idempotencyKey with your record so a retry after a crash reuses it. See Idempotency.
The actions
swap, limit_order, withdraw, transfer and shield need a grant and an Idempotency-Key, and respect the timelock, the schedule and owner_approval. Cancelling a limit order needs a grant only: see Limit Orders. swap, limit_order, withdraw and transfer also need assets in the policy that include the asset, and respect per-asset limits and max_actions_per_hour. The agent’s account is its confidential balance, so swap, limit_order, transfer and withdraw run on it by default, and they need confidential: true in the policy. A request never falls back to a public route. See The confidential balance.
Amounts are atomic-unit strings. Assets are asset_id values from GET /v1/tokens. Every amount that moves funds (swap, withdraw, transfer, shield) and a limit order’s quantity must be greater than zero, or the request fails with validation_failed. A recipient that is not a valid address for its chain is refused with invalid_address (400) before the policy is read.
Swap
Limit order
Places a resting order on the agent’s NEAR Intents balance and funds it in full. It fills when the market reaches its price, or is cancelled or reaches its deadline.SUCCESS means the order is funded and resting, not filled. Limit Orders has the fields, the price bound, cancelling and the errors.
Withdraw
Sends funds out of NEAR Intents to an address on another chain, or to a NEAR account withchain: "near".
NEAR (
nep141:wrap.near, or near) withdrawn to chain: "near" always arrives as native NEAR, from the public or the confidential balance, as it does from a NEAR wallet or 1Click. The recipient doesn’t need to register on wrap.near, and exchanges and payment invoices receive the NEAR they expect. A confidential withdrawal is paid out by 1Click, which unwraps it. There is no option to withdraw wNEAR to chain: "near".A non-USDC asset withdrawn to
chain: "hypercore" lands in HyperCore spot as that asset, not as USDC perps margin, and the preview warns with asset_delivered_as_is. Swap to USDC first to fund perps.Transfer
Sends funds to another NEAR Intents account.Shield
shield moves an asset from the legacy public balance to the confidential balance. It takes { asset, amount }. See Funding. unshield still exists, but every request is refused with public_balance_exit_only.
The confidential balance
The agent’s account is its confidential balance, andconfidential defaults to true on every action. On swap, limit_order, transfer and a deposit address, confidential: false is refused with public_balance_exit_only (400, layer request, not retryable), live and dry, before any provider call. A withdraw with confidential: false is allowed: it pays from the legacy public balance. Work already recorded on the public balance, such as a held or in-flight request or a resting limit order, still settles, and a replay of its Idempotency-Key returns its record.
A missing or invalid grant is reported before this refusal. A policy refusal is not: a public swap, limit order or transfer that the policy would refuse too still answers public_balance_exit_only, because no policy can allow it. A public withdrawal that the policy forbids stays policy_action_denied.
Receipts and notes
A transfer, withdrawal, swap, limit order or deposit that settles also gets a receipt: a leaf in the transparency log that a payee can verify without an API key. Shield and the native signing routes get none. A transfer or withdrawal takes an optionalreference that the receipt carries. Every one of those actions, and deposit, takes an optional private note that is never sent to the provider or on chain and never appears in a receipt.
Quote before you run
dry: true on swap, limit-orders and withdraw returns 200 with a quote instead of running the action. It runs nothing, charges no budget and reserves none. The account’s rules do not refuse it: each one the live request would fail comes back in warnings. An unlisted asset or an invalid recipient does refuse it, with 400. An archived, deleted or frozen account gets no quote. See Account rules in a quote.
- A swap quote needs only your partner API key. It takes no
Idempotency-Key. - A limit-order preview needs only your partner API key and takes no
Idempotency-Key. It makes no provider call: the API prices the order itself. - A withdrawal preview needs
X-Grant-Token, anIdempotency-Keyand a policy that allows the withdrawal to that destination, like the withdrawal itself. The same key returns the same preview for 60 seconds; after that,withdraw_preview_expiredmeans preview again under a new key. The API sets no preview cap: every preview that reaches the provider, rule warning or not, counts toward the wallet’s 1,000 custody operations per rolling 24 hours, and the provider’sprovider_limit_reachedis the only quota refusal. A preview the provider fails can be sent again under the same key. The TypeScript SDK creates the key from 0.5.2.
{ "dry": true, "type": "swap" | "limit_order" | "withdraw", "quote": { … }, "spend": { … }, "fee": { … } | null, "warnings": [ … ], "owner_approval": boolean }.
Account rules in a quote
The account’s rules do not refuse a quote or preview. On a usable account it goes to the provider and returns200 with the quote. It also checks what the live request would face, in this order: the policy’s action switch and destination rule, its assets, the per-transaction limit, the schedule at the time the request would run (after timelock_ms), the USD budget, then per-asset limits and max_actions_per_hour, and last the balance. Each one the live request would fail is a warnings entry whose code is the code that request would get: policy_action_denied, policy_destination_denied, policy_asset_denied, policy_transaction_limit_exceeded, policy_schedule_denied, spend_price_unavailable, spend_budget_exceeded, policy_limit_exceeded, policy_action_limit_exceeded, policy_usage_unavailable or insufficient_balance. A limit-order preview also warns with amount_too_low (the order is worth under 0.1 USD), policy_price_deviation_exceeded and market_price_unavailable: see Limit Orders. A policy_schedule_denied warning also carries available_at, when the schedule next allows the request. An insufficient_balance warning carries available_amount, what the balance can still spend in atomic units. The live request is refused with these codes. The quote only warns.
The request’s own checks do refuse the quote. An asset that GET /v1/tokens does not list (unsupported_token) or a recipient that is not a valid address (invalid_address) answers 400, as it does for the live request, because no quote can answer it. It is refused before any provider call.
An archived, deleted or frozen account gets no quote, because an account that can do nothing has no use for one. A swap quote and a withdrawal preview are both refused with 409 and the code the live request gets: agent_archived, agent_deleted or wallet_frozen. The refusal reaches no provider.
Read warnings before you execute. If any warning is one of these codes, the live request will be refused with that code. Tell the user, reduce the amount, wait (resets_at in spend.budget, or available_at) or ask the owner to change the policy. A quote without such a warning is a snapshot, not a guarantee. Other work can use the budget first, and a request held by timelock_ms is charged only when it is dispatched.
What happens to a call
Every execution passes these checks. A refusal names the layer that blocked it. Only a change to that layer lifts it. Adry: true quote or preview is not refused by these checks. It lists each one the live request would fail in warnings, under the same code.
Calls refused before the action is admitted leave nothing behind, so you can retry the same key after fixing the cause. That includes a new execution that exceeds the USD budget or a per-asset limit: if it runs now it is a plain refusal with no operation, no
FAILED status and no charge, and the same key refuses again while the cause lasts. Refusals recorded for an admitted action (for example wallet_busy, a request that lost a concurrent race at the dispatch commitment, or a timelocked execution released over budget or a limit) are replayed under the same key. After the cause is fixed, send a new Idempotency-Key, unless the response carries meta.retryable: then the same key works after Retry-After. The error’s layer says whose rule it was: Error layers.
The dispatch commitment
An execution has a single point of no return, its dispatch commitment. The API reaches it right before it hands the action to the agent wallet, anddispatch_committed_at in the status shows when. The USD charge is decided in the same step. Before it, revoking a grant or an API key stops the action. After it, nothing can recall it. The one exception is a resting limit order, which you can still cancel: what is unfilled is refunded. See Limit Orders.
Idempotency
Every execution needs anIdempotency-Key: 8 to 128 characters from A-Za-z0-9._:-. Retry with the same key and the same body to get the same operation back. Keys are scoped to the grant, and a new key is new work, so never use one to retry something UNCERTAIN or already dispatched. Status & Errors has the full rules.
The SDK creates a key when you omit one and never retries a write by itself. To survive a crash, create the key, save it, then send:
One money action per wallet
The agent wallet runs one money action at a time. A second action that arrives while one is running is refused withwallet_busy (409). Wait for the first to settle, then send the second with a new Idempotency-Key, unless the response carries meta.retryable (then retry with the same key after Retry-After).
Timelock and the queue
When the policy hastimelock_ms above zero, every execution except a deposit waits that long before it runs. The call returns at once with the status QUEUED.
GET /v1/agents/{agent_id}/executions/scheduledlists what the timelock holds, earliest release first, as{ correlation_id, execute_after, state, action }.stateiswaitingordispatching. Page withcursorandlimit.- The owner cancels a
waitingexecution with anexecution_cancelintent that names itscorrelation_id. Once it isdispatching, the API answersexecution_not_cancellable. - When the delay ends, the API checks the grant, the API key and the policy again. If any changed in the meantime, for example the owner revoked the grant or changed the policy, the execution fails with
authorization_stale. Nothing was sent. - The schedule is judged when the action would run: after the delay for a new request, and again at release. Outside the schedule, the action is refused with
policy_schedule_deniedandmeta.available_at. - The USD budget counts an execution when it dispatches, not when it was queued. A queued execution reserves nothing.
- A queued limit order’s price is judged against the market again at release, so the market can move out of the order’s bound while it waits. If a price for either asset is briefly unavailable at release, the API tries the order again later instead of failing it.
Owner approval
When the policy hasowner_approval: true, every execution waits for the owner’s vote before it moves funds. Deposits do not wait. The status reads PENDING_APPROVAL and details.approval_id names the request.
1
List what waits for approval
{ data, truncated } and lists at most 100 approvals. A listing may be up to 10 seconds old. A vote always reads its approval live. A listing that cannot finish within 20 seconds fails with provider_unavailable (503): send it again.At most 100 requests from one agent wait for the owner at once. The next is refused with approval_backlog_full (409, and a dry run’s warning) before anything is sent, and passes once the owner approves or rejects one. A request the API already failed takes no place. truncated is therefore true only when the provider holds approvals the API did not create; approve or reject some to see the rest. listApprovals returns the list without the flag.A limit order’s price is judged against the market when it is placed, and again when a timelock or the owner’s approval releases it.GET /v1/agents/{agent_id}/approvals/{approval_id} returns one request. Show request_type and request_data to the owner. An approval has an expires_at. After it, the approval’s status is EXPIRED, the execution fails with failure_code expired and details.never_executed, its budget charge is released, and it leaves the list. An execution whose authority lapsed before the vote (the grant was revoked or expired, or the policy or owner changed) ends the same way with authorization_stale. See Approval votes.2
The owner votes
The vote is an owner intent:
approval_vote with the approval_id and a verdict of approve or reject. It needs the owner’s signature and no grant.owner_approval works for NEAR and passkey owners, and for EVM owners on deployments that enable EVM owners. EVM and passkey owners vote through their 0s wallet, with an EIP-712 or WebAuthn wallet authorization. While EVM is off, new EVM owners are refused at agent_create with owner_type_unsupported. Read supported_owner_types from GET /v1/network.approval_signature_invalid (401), approval_not_allowed (403, the wallet policy does not admit the owner key or 0s wallet as approver), approval_vote_unsupported (409, the provider lists no such wallet build for this owner on this network, nothing was stored, ask the operator before voting again), approval_already_voted (409), approval_closed (409) and approval_not_found (404). See Status & Errors.
Recover an uncertain execution
MostUNCERTAIN executions need no action. Keep polling the same correlation_id. Never resend one under a new key.
POST /v1/agents/{agent_id}/recover dispatches an UNCERTAIN execution once, but only when the API can prove it never reached the agent wallet: it has no provider reference and no dispatch commitment. Recovery is not an override for an action whose outcome is unknown.
You send exactly what the first call sent: the same Idempotency-Key, the grant token of the grant that admitted it, the same API key, and the original body as request with its type.
Recovery runs the checks again, so a revoked grant or a changed policy refuses it. A limit order is recovered with
type: "limit_order" and its original body. Deposits cannot be recovered. Neither can a native NEAR transfer or call, or a deletion: they have no resume, and wait for the sweep that fails them after an hour.
Next steps
Status & Errors
Status values, recovery rules and every error code.
Limit Orders
Rest an order at a price, follow its fills and cancel it.
Policies & Limits
The rules every execution is checked against.