Overview
Use this cookbook when you want 1tx to handle:
instrument discovery,
quote generation,
source-chain selection,
calldata generation,
and cross-chain relay tracking.
This is the recommended integration path for most apps.
Prerequisites
an API key for discovery and monitoring endpoints,
a bearer token for transaction-building endpoints,
a wallet connection in your frontend,
a way to send EVM transactions, such as viem or wagmi.
Recipe 1: Build and Execute a Same-Chain Buy
Step 1: Fetch the instrument
const API_BASE = 'https://api.1tx.fi/api/v1' ;
const instrumentsResponse = await fetch ( ` ${ API_BASE } /instruments` , {
headers: {
'X-API-Key' : apiKey ,
},
});
const instruments = await instrumentsResponse . json ();
const instrument = instruments . data . find (
( item : { symbol : string ; chainId : number }) =>
item . symbol === 'USDC' && item . chainId === 8453 ,
);
Step 2: Build the transaction bundle
const bundleResponse = await fetch ( ` ${ API_BASE } /transactions/buy` , {
method: 'POST' ,
headers: {
Authorization: `Bearer ${ jwt } ` ,
'X-API-Key' : apiKey ,
'Content-Type' : 'application/json' ,
},
body: JSON . stringify ({
userAddress ,
instrumentId: instrument . instrumentId ,
amountUsdc: '1000.00' ,
slippageBps: 50 ,
}),
});
const bundle = await bundleResponse . json ();
Step 3: Execute the returned transactions in order
import { createWalletClient , custom , parseEther } from 'viem' ;
import { base } from 'viem/chains' ;
const walletClient = createWalletClient ({
chain: base ,
transport: custom ( window . ethereum ),
});
for ( const tx of bundle . transactions ) {
const hash = await walletClient . sendTransaction ({
account: userAddress ,
chain: base ,
to: tx . to ,
data: tx . data ,
value: BigInt ( tx . value ),
});
console . log ( `sent ${ tx . type } ` , hash );
}
The bundle may contain either:
a single router transaction, or
an approval followed by the router transaction.
An approval is only included when current allowance is insufficient. If it is present, it must be mined before the deposit transaction is sent.
Recipe 2: Build and Monitor a Cross-Chain Buy
For cross-chain buys, the source-chain router transaction is still built through POST /transactions/buy, but completion is tracked through the CCTP API.
Step 1: Build the bundle
const bundleResponse = await fetch ( ` ${ API_BASE } /transactions/buy` , {
method: 'POST' ,
headers: {
Authorization: `Bearer ${ jwt } ` ,
'X-API-Key' : apiKey ,
'Content-Type' : 'application/json' ,
},
body: JSON . stringify ({
userAddress ,
instrumentId: '0x0000a4b194d4938ed6aab5bdbac7ca4b622f3639b1bca1b8b9c3271403d3b1b5' ,
amountUsdc: '2.20' ,
}),
});
const bundle = await bundleResponse . json ();
if ( ! bundle . isCrossChain ) {
throw new Error ( 'expected a cross-chain route' );
}
Step 2: Execute the source-chain transactions
let sourceTxHash : `0x ${ string } ` | null = null ;
for ( const tx of bundle . transactions ) {
const hash = await walletClient . sendTransaction ({
account: userAddress ,
to: tx . to ,
data: tx . data ,
value: BigInt ( tx . value ),
});
sourceTxHash = hash ;
}
The final transaction in the bundle is the router buy() transaction. Use that hash as the source transaction hash to monitor.
Step 3: Poll relay status by source tx hash
type RelayStatus = {
jobId : string ;
status : 'pending' | 'waiting_attestation' | 'redeeming' | 'success' | 'failed' ;
sourceTxHash : string ;
sourceChainId : number ;
destinationChainId : number ;
destinationTxHash ?: string ;
error ?: string ;
};
async function waitForCrossChainCompletion ( sourceTxHash : string ) : Promise < RelayStatus > {
while ( true ) {
const response = await fetch ( ` ${ API_BASE } /cctp/relay/tx/ ${ sourceTxHash } ` , {
headers: {
'X-API-Key' : apiKey ,
},
});
if ( response . status === 404 ) {
await new Promise (( resolve ) => setTimeout ( resolve , 5000 ));
continue ;
}
if ( ! response . ok ) {
throw new Error ( `relay lookup failed: ${ response . status } ` );
}
const relay = ( await response . json ()) as RelayStatus ;
if ( relay . status === 'success' ) {
return relay ;
}
if ( relay . status === 'failed' ) {
throw new Error ( relay . error || 'cross-chain relay failed' );
}
await new Promise (( resolve ) => setTimeout ( resolve , 10000 ));
}
}
Step 4: Show destination completion
const relay = await waitForCrossChainCompletion ( sourceTxHash ! );
console . log ( 'destination tx:' , relay . destinationTxHash );
A brief 404 Job not found immediately after source-chain confirmation is expected. The relay job is created asynchronously when the bridge event webhook is ingested.
Recipe 3: Estimate CCTP Fees Before Building
If you want to display cross-chain transfer mode estimates before submitting a buy, use the CCTP fee endpoint.
const feeResponse = await fetch ( ` ${ API_BASE } /cctp/fee/6/3` , {
headers: {
'X-API-Key' : apiKey ,
},
});
const fees = await feeResponse . json ();
console . log ( fees . fastFeeBps , fees . standardFeeBps );
Use GET /cctp/config to map chain IDs to Circle domains.
Common Pitfalls
Missing bearer auth on POST /transactions/buy or POST /transactions/sell
Sending transactions out of order
Treating an early 404 from /cctp/relay/tx/:sourceTxHash as a permanent failure
Reusing an expired bundle after expiresAt
Formatting human-readable amounts incorrectly before calling the API
Next Steps
Transactions API Full request and response shapes for transaction bundles
CCTP API Relay monitoring and fee estimation endpoints