Skip to main content

Welcome to the Trails API

The Trails API enables seamless cross-chain token swaps, deposits, payments, and smart contract executions in a simplified interface with the Trails protocol. This guide walks you through the complete flow from requesting a quote to executing a transaction.
Get Your API Key: Join the Trails Telegram group to request your API access key.

Core Workflow

Every interaction through Trails follows this four-step process:
1

Get Wallet Balance

Before requesting a quote, fetch the user’s token balances using a multichain Indexer to display total available tokens and amounts for the user to select:
const balancesResponse = await fetch('https://indexer.sequence.app/rpc/IndexerGateway/GetTokenBalances', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-Access-Key': 'YOUR_API_KEY'
  },
  body: JSON.stringify({
    accountAddress: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
    includeMetadata: true
  })
});

const { balances } = await balancesResponse.json();

// Display balances in your UI for user to select tokens, amount, and routes.
console.log('Available tokens:', balances);
2

Get a Quote

Request a quote to see rates, fees, and routing options for your transaction.
const quoteResponse = await fetch('https://trails-api.sequence.app/rpc/Trails/QuoteIntent', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-Access-Key': 'YOUR_API_KEY'
  },
  body: JSON.stringify({
    ownerAddress: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb', // sender address
    originChainId: 42161, // Arbitrum One
    originTokenAddress: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', // USDC
    originTokenAmount: 100000000, // 100 USDC (6 decimals)
    destinationChainId: 8453, // Base
    destinationTokenAddress: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', // USDC on Base
    destinationToAddress: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb', // recipient
    tradeType: 'EXACT_INPUT',
    options: {
      slippageTolerance: 0.5, // 0.5%
      quoteProvider: 'RELAY'
    }
  })
});

const { intent, gasFeeOptions } = await quoteResponse.json();

console.log('Quote received:', {
  fromAmount: intent.quote.fromAmount,
  toAmount: intent.quote.toAmount,
  totalFees: intent.fees.totalFeeUsd,
  expiresAt: intent.expiresAt
});
Key Parameters:
  • ownerAddress: User’s wallet address
  • originChainId & destinationChainId: Source and destination chains
  • originTokenAddress & destinationTokenAddress: Token contracts
  • originTokenAmount: Amount to swap (in token’s smallest unit)
  • tradeType: EXACT_INPUT (specify input) or EXACT_OUTPUT (specify output)
Use EXACT_INPUT when you know how much you want to spend, and EXACT_OUTPUT when you know exactly how much you need to receive.
3

Commit the Intent

Lock in the quote by committing the intent from the previous response which will reserve the rates.
const commitResponse = await fetch('https://trails-api.sequence.app/rpc/Trails/CommitIntent', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-Access-Key': 'YOUR_API_KEY'
  },
  body: JSON.stringify({ intent })
});

const { intentId } = await commitResponse.json();

console.log('Intent committed:', intentId);
You cannot change the contents of the intent, or the server will reject the commit. The intent object contains everything needed to relay and execute the intent.
Committed intents expire after a set period (check intent.expiresAt). Execute before expiration or request a new quote.
4

Execute the Transaction

Execute the intent using one of two mutually exclusive methods - either a normal transfer to the intent address or a permit operation if a user wants to pay in a non-native gas token:
  • Transfer Flow
  • Alternative Fee Token Flow
Send tokens to the intent deposit address, then call ExecuteIntent with the transaction hash:
import { createWalletClient, createPublicClient, http } from 'viem';
import { base, arbitrum, mainnet } from 'viem/chains'; // add other chains to support

// Create clients (or use wagmi hooks: useWalletClient, usePublicClient)
const walletClient = createWalletClient({
  chain: mainnet, base, arbitrum
  transport: http()
});

const publicClient = createPublicClient({
  chain: mainnet, base, arbitrum
  transport: http()
});
// Send tokens to the intent address (where Trails will execute from)
const intentAddress = intent.depositTransaction.toAddress;
const tokenAddress = intent.depositTransaction.tokenAddress;
const amount = intent.depositTransaction.amount;

const depositTxHash = await walletClient.sendTransaction({
  to: intentAddress,
  value: 0n, // No native ETH sent (0 for ERC20 transfers, or ETH amount if sending native token)
  data: encodeERC20Transfer( // Use your favorite library such as viem to encode ERC20 transfer
    tokenAddress,
    intentAddress,
    amount
  )
});

// Wait for transaction confirmation using viem
const receipt = await publicClient.waitForTransactionReceipt({
  hash: depositTxHash
});

// Then execute the intent with the deposit transaction hash and intentId
const executeResponse = await fetch('https://trails-api.sequence.app/rpc/Trails/ExecuteIntent', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-Access-Key': 'YOUR_API_KEY'
  },
  body: JSON.stringify({
    intentId,
    depositTransactionHash: depositTxHash
  })
});

const { intentStatus } = await executeResponse.json();
console.log('Execution started:', intentStatus);
5

Monitor Completion

Wait for the transaction to complete using the streaming endpoint:
async function waitForCompletion(intentId: string) {
  while (true) {
    const waitResponse = await fetch(
      'https://trails-api.sequence.app/rpc/Trails/WaitIntentReceipt',
      {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'X-Access-Key': 'YOUR_API_KEY'
        },
        body: JSON.stringify({ intentId })
      }
    );

    const { intentReceipt, done } = await waitResponse.json();

    console.log('Status:', intentReceipt.status);

    // If done is true, the intent has reached a terminal state
    if (done) {
      if (intentReceipt.status === 'SUCCEEDED') {
        console.log('✅ Transaction completed!');
        console.log('Deposit TX:', intentReceipt.depositTransaction.txnHash);
        console.log('Origin TX:', intentReceipt.originTransaction.txnHash);
        console.log('Destination TX:', intentReceipt.destinationTransaction.txnHash);
        return intentReceipt;
      } else {
        throw new Error('Transaction failed: ' + intentReceipt.originTransaction.statusReason);
      }
    }
    
    // If not done, the endpoint will have waited internally before returning until completion
  }
}

const receipt = await waitForCompletion(intentId);
Now you have an end to end API integration with Trails for seamless chain abstraction flows! You can continue this initial integration for specific use cases such as:
  • x402 Payments
  • AI Agents
  • Server-side currency conversion & settlement
  • Fund, Swap, Earn, or Pay for any application
  • Pass in optional calldata to call any smart contract function

Next Steps

Explore additional endpoints to enhance your integration:

Transaction Management

  • SearchIntents - Search and filter intents by status, user, chain, or date range
  • GetTokenPrices - Get current USD prices for tokens to display values in your UI

Support

Need help? Join our community: