swap, withdraw, transfer, shield or unshield. 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.
At a glance
- Headers:
X-API-Key,X-Grant-Tokenand anIdempotency-Keyon every execution and withdrawal preview. A swap quote (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
All five actions need a grant and an
Idempotency-Key, and respect the timelock, the schedule and owner_approval. swap, withdraw and transfer also need assets in the policy that include the asset, and respect per-asset limits and max_actions_per_hour. A confidential: true request also needs confidential: true in the policy and never falls back to a public route.
Amounts are atomic-unit strings. Assets are asset_id values from GET /v1/tokens. Every amount that moves funds (swap, withdraw, transfer, shield, unshield) 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
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 and unshield
shield moves an asset from the public balance to the confidential balance. unshield moves it back. Both take { asset, amount }. See Funding.
Quote before you run
dry: true on swap 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 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. Each agent gets 20 previews per rolling 24 hours. Every preview that reaches the provider uses one, including ones that carry a rule warning; over that,withdraw_preview_limit_reached. A preview the provider refuses (for exampleroute_unavailable), an outage and an uncertain attempt stay counted. Only an answer the provider gives before running anything (its rate limiter’s 429,wallet_busy,provider_limit_reached) gives its attempt back, and the same key may try again. The TypeScript SDK creates the key from 0.5.2.
{ "dry": true, "type": "swap" | "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 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_schedule_denied, spend_price_unavailable, spend_budget_exceeded, policy_limit_exceeded, policy_action_limit_exceeded, policy_usage_unavailable or insufficient_balance. 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 and uses no preview attempt.
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 and uses no preview attempt.
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.
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.
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
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 status is EXPIRED.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. Deposits cannot be recovered.
Next steps
Status & Errors
Status values, recovery rules and every error code.
Policies & Limits
The rules every execution is checked against.