1Click API for apps
Integrate the NEAR Intents 1Click Swap API: tokens, dry and live quotes, deposit addresses, status polling, refunds, app fees, and when to use raw intents.
Advanced8 min read3-question check
1Click turns everything in the previous lessons into a REST API. You request a quote; for an ORIGIN_CHAIN deposit it returns a deposit address on the user’s chain. The user sends funds there from any wallet, and 1Click bridges, gets solver quotes, settles in intents.near and delivers to the recipient, or refunds. Your app never holds keys or liquidity. Docs: 1Click Swap API, OpenAPI spec.
Endpoints#
| Call | Purpose |
|---|---|
GET /v0/tokens | Supported assets: assetId, blockchain, symbol, decimals, price, contractAddress |
POST /v0/quote | Quote. dry: true previews without a deposit address; dry: false returns quote.depositAddress (+ depositMemo on memo chains such as Stellar) |
POST /v0/deposit/submit | Optional: tell 1Click the deposit tx hash so it is picked up faster |
GET /v0/status?depositAddress=… | PENDING_DEPOSIT, KNOWN_DEPOSIT_TX, PROCESSING, INCOMPLETE_DEPOSIT, then SUCCESS, REFUNDED or FAILED, with swapDetails |
POST /v0/generate-intent, POST /v0/submit-intent | For depositType: INTENTS: the user signs an intent instead of sending a deposit |
Base URL https://1click.chaindefuser.com. Send your partner JWT as X-API-Key (or Authorization: Bearer). Without it, quotes still work but carry an extra 0.25% platform fee. There is no testnet: test with small amounts.
Quote, show, deposit, track#
import { OneClickService, OpenAPI, QuoteRequest } from '@defuse-protocol/one-click-sdk-typescript'
OpenAPI.BASE = 'https://1click.chaindefuser.com'
OpenAPI.TOKEN = process.env.ONECLICK_JWT // keep the key on your server
// Pin asset IDs from /v0/tokens; never build them by hand
const USDC_SOL = 'nep141:sol-5ce3bf3a31af18be40ba30f721101b4341690186.omft.near'
const USDC_NEAR = 'nep141:17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1'
export async function createSwap(userSolanaAddress: string, nearRecipient: string) {
const request: QuoteRequest = {
dry: true,
swapType: QuoteRequest.swapType.EXACT_INPUT,
slippageTolerance: 100, // basis points: 1%
originAsset: USDC_SOL,
depositType: QuoteRequest.depositType.ORIGIN_CHAIN,
destinationAsset: USDC_NEAR,
amount: '25000000', // 25 USDC, smallest units, integer string
refundTo: userSolanaAddress,
refundType: QuoteRequest.refundType.ORIGIN_CHAIN,
recipient: nearRecipient,
recipientType: QuoteRequest.recipientType.DESTINATION_CHAIN,
deadline: new Date(Date.now() + 30 * 60_000).toISOString(),
appFees: [{ recipient: 'fees.myapp.near', fee: 30 }], // your 0.3%, paid inside Intents
}
// 1. Preview: price and time estimate, no deposit address
const preview = await OneClickService.getQuote(request)
console.log(preview.quote.amountOutFormatted, preview.quote.timeEstimate)
// 2. Live quote once the user confirms
const live = await OneClickService.getQuote({ ...request, dry: false })
await saveQuote(live) // keep the WHOLE response, including `signature`, for disputes
return live.quote // depositAddress, depositMemo?, amountIn, minAmountOut, deadline…
}
const TERMINAL = new Set(['SUCCESS', 'REFUNDED', 'FAILED'])
export async function track(depositAddress: string) {
for (;;) {
const s = await OneClickService.getExecutionStatus(depositAddress)
if (TERMINAL.has(s.status)) return s // swapDetails: amountOut, refundedAmount, refundReason…
if (s.status === 'INCOMPLETE_DEPOSIT') notifyUser('Deposit is below the quoted amount')
await new Promise((r) => setTimeout(r, 5_000))
}
}
declare function saveQuote(q: unknown): Promise<void>
declare function notifyUser(msg: string): voidShow the user depositAddress, the exact amountIn, the chain, and the deadline, and warn them not to send another asset or from an exchange that batches withdrawals late. If they paid from a wallet you control in the browser, call submitDepositTx({ depositAddress, txHash }) right after sending.
Swap types#
swapType | amount means | Deposit handling |
|---|---|---|
EXACT_INPUT | What the user sends | Less than amountIn → refunded at the deadline; more → swapped, excess refunded |
EXACT_OUTPUT | What the recipient gets | Quote returns amountIn (with slippage buffer) and minAmountIn; unused input refunded |
FLEX_INPUT | A target; slippage applies on both sides | Anything ≥ minAmountIn is swapped; good when the exact amount is unknown |
ANY_INPUT | "0", origin 1cs_v1:any | Standing collector, any token, swept once it clears ~$1,000; authorized partners only, no refunds |
Refunds, deadlines and fees#
- `deadline` must leave time for the deposit to confirm: minutes for EVM chains or Solana, around an hour for Bitcoin. After it, unfilled deposits are refunded to
refundTo. - `refundType`:
ORIGIN_CHAINreturns funds to the user’s address on their chain;INTENTSrefunds to an account insideintents.near. The quote’srefundFeeis deducted. - Fees (October 2026): protocol fee 0.0001% on-chain; 1Click adds 0.20% with an API key (0.01% on stablecoin and same-asset routes), or 0.25% more without one.
appFeesyou add are split with 1Click by default (it keeps at least 20 bps, or 1 bp on stablecoin routes); see Fees. - `withdrawFee` for the destination chain is already included in
amountOut.
1Click or raw intents?#
| Situation | Choose |
|---|---|
| User pays from a wallet on another chain (deposit address flow) | 1Click: it is the only piece that hands you deposit addresses with refunds |
Users already hold balances in intents.near and sign with their wallet | 1Click with depositType: INTENTS, or the Intents SDK for custom intent batches |
You need intents 1Click does not build: P2P token_diff, batched transfers, NFT moves, auth_call | Intents SDK / direct execute_intents |
| You quote prices yourself or run inventory | Solver relay as a market maker |
Check yourself
3 questions · progress saved in this browser