native block. Each one has its own route under POST /v1/agents/{agent_id}/native/….
The agent’s addresses
GET /v1/agents/{agent_id}/wallet returns the agent’s addresses.
The EVM and Solana keys are separate keys. Nothing they sign can reach the agent’s own NEAR Intents balance, the one held under
near_account_id. The NEAR key can never sign for, call or send to the NEAR Intents contracts (intents.near, intents.far), so it can’t reach that balance either.
The EVM and Solana keys do have a NEAR Intents account of their own, apart from the agent’s balance: the lowercase 0x EVM address, and the lowercase hex of the 32-byte key behind solana_account_id. Funds held there are what sign-intent spends.
The EVM routes take a chain: ethereum, polygon, base, arbitrum, optimism, bsc, avalanche, hyperevm or hood. The address is the same on all of them.
Turn methods on
The owner addsnative to the policy. Read the current policy, set native, and send the complete policy as a policy_update:
- Omitted means every method is off. When
nativeis present, every key in it is required exceptsolana.sign_in,evm.sign_intent,solana.sign_intentandsolana.sign_transfer, which may be left out (off). An empty list is off. - Lists hold up to 256 unique NEAR accounts (
near.sign_message,near.call), up to 256 unique lowercase hosts, with their port if they have one (solana.sign_in), or up to 64 domain entries, unique by domain (evm.sign_typed_data). intents.nearandintents.farcan never be listed for the NEAR key. A policy that lists them is refused withvalidation_failed. The EVM and Solana keys reach them only throughsign-intent.- Transactions are checked before anything is signed.
native/evm/sign-transactiontakes one serialized unsigned EIP-2718 typed or EIP-155 legacy transaction whose chain id is that ofchain.native/solana/sign-transactiontakes one legacy or v0 transaction message. Anything else is refused withvalidation_failed, so neither route signs a message. - Every grant on the agent can use a method once it is on. Revoking a grant stops only that grant.
- The wallet provider enforces the switches too, so changing
nativeis an on-chain policy change. A NEAR owner signs it asnep366, the API pays the gas, and the policy cooldown applies. A method works once the revision isAPPLIEDandprovider_policy_syncedistrue. - The owner’s
preview.summarynames every method the policy switches on and says which of the account’s money rules they skip.
What applies
Every native request needs a live grant token (X-Grant-Token) and an applied policy, and is refused while the agent is frozen. The policy switch is the fence, with one money rule beside it: destinations binds the NEAR a transfer sends or a call attaches, and the recipient of a Solana sign-transfer. sign-intent has its own rules instead.
Every native request is recorded in the agent’s history (
GET /v1/agents/{agent_id}/history) as type native. Signatures are not stored in it. See History.
Sign a message (NEAR)
An agent can sign a NEP-413 message with its NEAR key, which is how a site or dApp logs in a NEAR account (signMessage). The service, the relying party, issues a message and a random 32-byte nonce and names itself as the recipient. Your backend has the API sign exactly those, and the relying party checks the signature. It moves no money.
Turn it on by listing the relying party in native.near.sign_message. An entry is a NEAR account or a domain (printable ASCII, up to 128 characters), matched exactly, with no wildcard.
This route signs a NEP-413 message only. It never signs a transaction or an intent, and never for the NEAR Intents contracts (
intents.near, intents.far), in any letter case.Sign a message
Your backend receivesmessage, recipient and nonce from the relying party and sends them unchanged with the grant token. The API key alone is refused with agent_grant_required.
callback_url is not supported. The response is 200. It answers at once, with nothing to poll. correlation_id names the history record.
Verify a signature (relying party)
The relying party checks, in order:recipientis its own account or domain, andnonceis one it issued, unused.- The signature (base64) verifies with
public_keyover the NEP-413 payload: the exactmessageit issued, the decoded 32-bytenonce,recipient, and no callback URL. public_keycontrolsnear_account_id: a full-access key on that account, read atfinal. For an implicit account that doesn’t exist on-chain yet, the account ID is the hex of that key.
Send NEAR and call contracts
native/near/transfer sends native NEAR from the agent’s NEAR account. native/near/call calls a contract listed in native.near.call. Both run outside NEAR Intents, and the agent’s NEAR account pays the gas. Its native NEAR must cover the attached deposit plus the gas the call prepays, else the call is refused with custody_native_balance_required, which states both amounts when the provider does. Don’t attach the whole balance.
Call fields:
A request can be at most 65,536 bytes as JSON.
NEAR that a transfer sends, or a call attaches as its
deposit, must land where the policy’s destinations allows. The API judges it as a withdraw on chain near to the receiving account, and refuses it with policy_destination_denied (403) otherwise. budget, timelock_ms, schedule and owner_approval still don’t apply. A call can move anything the listed contract holds for the agent, whatever destinations says.
Both routes need an Idempotency-Key and answer 202 with a status of type native. Track it with GET /v1/status like any execution. The provider’s word does not settle it. Once the provider names a transaction (details.tx_hash), the API reads the outcome from NEAR alone:
PROCESSINGuntil two RPC operators agree that NEAR has finalized the transaction and that it is exactly the request: signed by the agent’s account, to the named recipient or contract, with the amount, method, arguments, gas (when given) and deposit asked for.details.provider_statusdoes not change this.SUCCESSonce they agree, withdetails.tx_hash.FAILEDwithnative_transaction_failedwhen the finalized transaction failed.failure_reasonhas the chain’s reason. A transaction the provider reports as broadcast and reverted keeps its realdetails.tx_hashandprovider_statusonchain_tx_failed, and readsFAILEDonce the chain confirms it. Nothing it asked for happened, and it is never resent.NEEDS_REVIEWwhen automatic settlement stopped:native_transaction_mismatch(the finalized transaction is not the request) ornative_transaction_unconfirmed(NEAR still does not know it 30 minutes after dispatch), among others. See Idempotency and review.
Sign EVM typed data
native/evm/sign-typed-data signs EIP-712 v4 typed data, as eth_signTypedData_v4 takes it, with the agent’s EVM key. It signs only under a domain the owner listed in native.evm.sign_typed_data.
TypeScript SDK
Each field is optional, but a domain names at least one. The rules:
- Field for field. A request’s
domainmust equal one listed entry: the same fields present, with the same values. A missing or extra field makes it another domain, which is refused. - Spelling.
chainIdmay be a number, a decimal string or a hex string. Addresses and salts compare lowercase. types.EIP712Domainmay be omitted, and the API derives it. When you send it, it must declare exactly the domain’s fields, in EIP-712 order (name,version,chainId,verifyingContract,salt), with their standard types.primaryTypeis the message type.EIP712Domainis refused.messageis at most 65536 bytes as canonical JSON. A larger one is refused withvalidation_failed.
domain with a field EIP-712 doesn’t define, a types.EIP712Domain that doesn’t match, or primaryType: "EIP712Domain" is refused with validation_failed. A well-formed domain that isn’t listed, or a primaryType the matching entry doesn’t allow, is refused with native_method_denied.
A protocol separates powers by domain. Hyperliquid trades under its Exchange domain and withdraws under HyperliquidSignTransaction. Listing Exchange lets the agent trade, but it never signs a HyperliquidSignTransaction withdrawal.
Limit the message types
A protocol often signs several powers under one domain. Hyperliquid’sHyperliquidSignTransaction domain signs HyperliquidTransaction:Withdraw, and also HyperliquidTransaction:SpotSend and HyperliquidTransaction:UsdSend, which pay any address. An entry can list the message types it allows in primary_types:
primary_typesis optional: 1 to 32 unique strings, each 1 to 256 characters, case-sensitive, neverEIP712Domain. An empty list is invalid. Omitted means any message type under the domain.- A request matches when its domain equals the entry’s domain field for field, and either the entry has no
primary_typesor the request’sprimaryTypeis listed. Otherwise it is refused withnative_method_denied. - One entry per domain. Entries are unique by domain, so one domain listed twice with different types is refused. Give one entry all its types.
Sign EVM messages and transactions
evm.sign_transaction gives full control of the EVM address. Nothing is broadcast: you send the signed transaction yourself.
native/evm/sign-message refuses bytes that are a NEAR Intents payload, in any encoding, with intents_payload_use_sign_intent (403). Use sign-intent for those. A deployment can turn this refusal off for both message routes. Where it is off, evm.sign_message amounts to full control of the key’s NEAR Intents account.
Sign Solana messages and transactions
solana.sign_transaction gives full control of the Solana address. Nothing is broadcast: you send the signed transaction yourself.
native/solana/sign-message also refuses bytes that are a NEAR Intents payload, with intents_payload_use_sign_intent (403). It signs any other bytes as sent, 32-byte digests included, and NEAR Intents accepts such a digest under other signature standards. solana.sign_message therefore amounts to full control of that key’s NEAR Intents account, the lowercase hex of its public key, with none of the checks sign-intent makes. Give it only to an agent the owner trusts with that account. For a login use solana.sign_in, and for a payment solana.sign_transfer or sign-intent.
Sign NEAR Intents payloads (EVM and Solana)
native/evm/sign-intent and native/solana/sign-intent let the agent’s EVM or Solana key authorize a NEAR Intents transfer from that key’s own Intents account. The answer is ready to publish to intents.near (execute_intents), to 1Click or to a solver relay. The EVM and Solana keys sign for the NEAR Intents contracts only this way.
Each key has its own rules, evm.sign_intent and solana.sign_intent, apart from sign_message. Neither opens the other’s route. The rules are the method’s own: the policy’s destinations, assets, limits, budget, timelock_ms, schedule and owner_approval never judge what it signs.
The sign-intent rules
Omitting
sign_intent turns the method off. Present means on.
receivers is the only rule that fits a plain transfer: a listed receiver is paid whatever the payload’s tokens say, from the Intents balance of that key’s account, and anyone who holds the payload can publish it until its deadline. There is no amount limit. A 1Click deposit is held to its quote instead: it is paid with exactly the quote’s amount, and the loss at the quote’s minimum output, fees included, is bounded by max_slippage_bps.What the payload must be
The body is{ payload, one_click_quotes? }. payload is the exact JSON text of { signer_id, verifying_contract, deadline, nonce, intents }, as JSON.stringify of that object writes it, with no whitespace and no repeated or unknown keys. The route signs the text it was given, so nothing can re-serialize it between your backend and the signature. one_click_quotes is for 1Click deposits (below).
- Intents.
intentsholds onlytransferintents (intent,receiver_id,tokens, and optionallymemo,msgandmin_gas), or none. Anything else, such asadd_public_key,token_diffor a withdrawal, is refused withintents_type_forbidden, andsource.pointernames the intent. - Signer.
signer_idis the signing key’s Intents account: the lowercase0xEVM address (wallet.evm_account_id, lowercased; a checksummed spelling is refused), or the lowercase hex of the 32-byte key behindwallet.solana_account_id. Otherwiseintents_signer_mismatch. - Contract.
verifying_contractisintents.nearorintents.far, and no receiver may be either of them (intents_contract_forbidden). - Deadline. An RFC 3339 UTC time ending in
Z, still ahead and at most 300 seconds after the API’s clock. A payload that pays only 1Click deposits (below) may be due up to 96 hours ahead, which is the deadline 1Click writes into the payloads it generates. Otherwiseintents_deadline_invalid. Anyone who holds a signed payload can publish it until then, so sign one that is due soon. - Nonce. Base64 of 32 bytes.
- Receivers. Every
receiver_idmust be a listed receiver, or one of at most 2 1Click deposit addresses per payload whose signed quote inone_click_quotespasses the checks below. Otherwisepolicy_destination_denied(403), with the reason indetail, before the wallet provider is called.
intents_payload_invalid (400), with the field in source.pointer.
1Click deposits
A receiver that is not listed must be a 1Click deposit address, and the request must carry the quote 1Click gave for it. Put up to 2 quotes inone_click_quotes, one per deposit address the payload pays outside sign_intent.receivers. Each is the answer of 1Click’s POST /v0/quote, sent unchanged (quote, quoteRequest, signature, timestamp and the rest). Only live quotes count: dry must be false.
The API checks 1Click’s ed25519 signature on each quote offline, against 1Click’s quote key ed25519:reYaWhvwu8Jzo3WUM3zhn6VrhuMEF4eADL17qtRVifc (the key the 1Click SDK’s verifyQuoteSignature uses). It does not call 1Click to read a deposit’s status. Confidential deposits (intents.far) work this way, and public ones (intents.near) need the quote too. It signs only when the quote:
- carries 1Click’s valid signature, and is live. A deposit with no quote, or a quote that is unsigned, altered or a dry run, is refused with
policy_destination_denied. A quote for an address the payload does not pay, or two quotes for one address, isvalidation_failed(/one_click_quotes/{i}/quote/depositAddress); - pays out where the owner allowed. With
one_click_deposits: to the payload’ssigner_idor to a listed receiver, inside NEAR Intents (recipientTypeis the contract’s account type). With awithdrawalsentry: to a listed address on the chain 1Click lists the quote’sdestinationAsseton (recipientTypeDESTINATION_CHAIN). The chain comes from 1Click’s token listing, never from the quote request. If the listing cannot be read, the request is refused withprovider_unavailable; - refunds the signing key (
refundTois thesigner_id), withrefundTypeanddepositTypeequal to the contract’s account type:INTENTSforintents.near,CONFIDENTIAL_INTENTSforintents.far; - has no
rebates,customRecipientMsg,virtualChainRecipientorvirtualChainRefundRecipient; - loses no more at
minAmountOutthanmax_slippage_bpsallows, by 1Click’s signed USD amounts. 1Click’s fee and slippage both count, andappFeesandslippageToleranceare not read. Request slippage clearly below the cap, such as 50 under a cap of 100: a quote at exactly the cap is refused, because fees and spread push it over; - is paid by one transfer of exactly the quote’s
amountInof itsoriginAsset, due no later than the quote’sdeadline. Onintents.farthe token is the confidentialimt:51e8f94d77b5e90dc9852ca6113771e11e8382ce69473a75e313e41665de7cbe:<originAsset>token, which is how 1Click generates it.
Idempotency-Key. A retry is judged again, and a refused payload never reaches the wallet provider. A deposit that 1Click has closed since the quote is not detected, and a payment that arrives late refunds to the signer.
A listed receiver as the quote’s recipient lets the key move confidential funds to another of the owner’s accounts, such as the agent’s NEAR account, to use /withdraw, where a plain transfer on intents.far has no public RPC to be published through.
Use it end to end
Your backend drives the whole flow with its own 1Click access. The Agent API only signs.1
Switch it on
The owner adds
sign_intent to native.evm or native.solana with a policy_update and waits until provider_policy_synced is true. List the owner’s own accounts and addresses only.2
Get a 1Click quote
Request a quote with
POST /v0/quote and dry: false. Use depositType INTENTS (or CONFIDENTIAL_INTENTS for intents.far), and set refundTo to the key’s Intents account (signer_id) with the same refundType. Set the recipient to the key’s own account, a listed receiver or a listed withdrawals address. Request slippage clearly below max_slippage_bps (50 under a cap of 100), and send no rebates or custom messages. Keep the whole answer, not only the depositAddress. See Signed Intent Execution.3
Generate the payload
Call 1Click’s
POST /v0/generate-intent with the depositAddress, signerId set to the key’s Intents account, and the standard the key signs: erc191 for the EVM key, raw_ed25519 for the Solana key. Its intent.payload is the text to sign.4
Sign it with the agent's key
Send
intent.payload unchanged to the Agent API, with the grant token and the quote from step 2 in one_click_quotes, also unchanged.5
Publish the signed data
Publish
signed_data as is, to 1Click with POST /v0/submit-intent (type: "swap_transfer", signedData: signed_data), to intents.near with execute_intents, or to a solver relay. The Agent API does not publish anything and records no outcome. Follow the swap in 1Click’s status for the depositAddress.The answer
The usual signature plussigned_data. For the EVM key, chain is ethereum, because personal_sign ignores the chain.
signature as r‖s‖v and v as 0 or 1. The second is the Solana key’s. Before it answers, the API checks the signature against the agent’s own key and the payload’s bytes: the address recovered from the ERC-191 signature must be evm_account_id, and the Ed25519 signature must verify under the Solana key. A signature that does not is discarded and the request is refused with provider_signature_invalid (502). The history record keeps the payload’s hash and the contract as target, not the payload.
Sign in with Solana
native/solana/sign-in is Sign-In-With-Solana with the agent’s Solana key, so a dApp or service can log the agent in as solana_account_id without solana.sign_message. Turn it on by listing the service’s domain in native.solana.sign_in: a lowercase host, with its port if it has one, matched exactly.
The API writes the message itself, as the wallet standard’s createSignInMessageText does, with the agent’s address. The signature is therefore a login for that domain and nothing else: the text is never JSON, so it is no NEAR Intents payload, and it is longer than 32 bytes, so it is no digest another signature standard signs.
TypeScript SDK
not_before, request_id and resources are not taken. A field that breaks these rules is refused with validation_failed, with the field in source.pointer. A domain that is not listed is native_method_denied.
The answer is the usual signature plus signed_message, the exact text that was signed. Give the relying party its UTF-8 bytes with signature and account_id. The API checks the signature over that text before it releases it. The history record keeps the domain as target and the request’s hash, not the message.
Send a Solana transfer
native/solana/sign-transfer signs one SOL or token transfer from the agent’s Solana key without solana.sign_transaction, which signs any transaction and gives full control of the address. It needs native.solana.sign_transfer: true. Nothing is broadcast: you send the signed transaction yourself, while its blockhash is still recent.
TypeScript SDK
recipient against destinations as a withdraw on chain sol, with no memo. It refuses with policy_destination_denied (403) before it calls the wallet provider, and signs the exact bytes it read. The message must be:
- a legacy or version 0 message with no address lookup tables, so every account is in it;
- signed by the agent only, which is also the fee payer, with no account appearing twice;
- exactly one transfer: a System Program
Transferfrom the agent torecipient, or one SPL Token or Token-2022TransferCheckedwith the agent as authority into the associated token account ofrecipientfor that mint.
CreateIdempotent of that account, paid by the agent, and one compute unit limit and one compute unit price whose priority fee is at most 1,000,000 lamports. Anything else, from a memo to a durable-nonce AdvanceNonceAccount, is refused with solana_transaction_forbidden (403), with the reason in detail, and nothing is signed.
The history record keeps the recipient as target, the amount (lamports, or the token’s atomic units) and the transfer as read in message_json: recipient, mint, token_program and amount.
The signature
The EVM and Solana routes answer200 with the signature. They take no Idempotency-Key and have nothing to poll. The signing call is the only place a signature is returned. Status and history return signature: null, for older records too.
Idempotency and review
A NEAR transfer or call takes anIdempotency-Key like any execution. The same key with the same body returns the same operation and never sends twice. See Idempotency.
If the provider’s answer is lost, the API sends the original request again under the same provider key, which returns the original request. It does this for 10 minutes after dispatch. After that the request reads NEEDS_REVIEW with failure_code native_request_unconfirmed, and it is never resent. Nothing can resume it: a native transfer or call has no recover.
NEEDS_REVIEW can also carry native_transaction_mismatch (NEAR finalized a transaction that is not the request), native_transaction_unconfirmed (NEAR still does not know the transaction 30 minutes after dispatch), provider_review_required, or provider_partially_failed when part of a call ran. In every case:
- Stop polling, and never send it again under a new key. It may have run.
- After the review is resolved, read the status with
refresh=true. See NEEDS_REVIEW.
Idempotency-Key. Each request signs again and records its own history entry.
History
Every native request is one history entry of typenative. Its details say what was asked of the key:
Turn it off
Remove an entry from a list, set a switch tofalse, or omit native to turn every method off. Each is an on-chain policy change, like turning it on. Freezing the agent stops every native route at once (wallet_frozen), but it cannot recall a signature already returned. A sign-intent payload signed before the freeze stays publishable until its own deadline, up to 96 hours for one that pays only 1Click deposits. Revoking a grant stops only that grant, and recalls nothing either.
Deleting the agent doesn’t sweep funds at its EVM or Solana address to the owner, and native NEAR left in its NEAR account goes to the sponsor. Move them first. See Deleting an agent.
Errors
signing_recipient_forbidden is checked first, before the grant and the policy.
See Status & Errors for the error format and every other code.