Quickstart

Go from zero to your first AI agent payment in under five minutes.

Quickstart

This guide walks you through installing the Axon SDK, initializing a client, sending your first payment, and checking its status. By the end you will have a working bot that can pay any on-chain address through your vault.

Prerequisites

Before you begin, make sure you have:

  • Node.js 20+ and npm (or pnpm / yarn)
  • A deployed Axon vault with USDC deposited (Deploy a Vault)
  • A registered bot key pair whose address is whitelisted on your vault (Register a Bot)
  • Your bot's private key available as an environment variable

If you do not have a vault yet, head to the Axon Dashboard and deploy one on Base Sepolia to follow along on testnet.

1. Install the SDK

npm install @axonfi/sdk

The package ships with full TypeScript types. No additional @types install is needed.

2. Configure Environment Variables

Create a .env file in your project root (or export the variables in your shell):

# Option 1: Encrypted keystore (recommended)
AXON_BOT_KEYSTORE_PATH=./axon-bot-0xABCD1234.json
AXON_BOT_PASSPHRASE=your-passphrase
 
# Option 2: Plain private key (not recommended)
# AXON_BOT_PRIVATE_KEY=0xYOUR_BOT_PRIVATE_KEY
 
AXON_VAULT_ADDRESS=0xYOUR_VAULT_ADDRESS
AXON_CHAIN_ID=84532

Axon never stores your bot private key — it is generated client-side and stays with you. And even if a bot key is compromised, the damage is bounded: bots can only sign expiring intents within your configured spending limits, cannot withdraw funds, and can be removed instantly. That said, you should still treat the key carefully. Never commit a plain private key to version control — an accidental git push exposes it to anyone with repo access. Use a secrets manager for plain keys, or use an encrypted keystore with the file and passphrase stored separately.

3. Initialize the Client

With an encrypted keystore file (recommended):

import { AxonClient, decryptKeystore } from '@axonfi/sdk';
import fs from 'fs';
 
const keystore = fs.readFileSync(process.env.AXON_BOT_KEYSTORE_PATH!, 'utf8');
const privateKey = await decryptKeystore(keystore, process.env.AXON_BOT_PASSPHRASE!);
 
const axon = new AxonClient({
  botPrivateKey: privateKey,
  vaultAddress: process.env.AXON_VAULT_ADDRESS as `0x${string}`,
  chainId: Number(process.env.AXON_CHAIN_ID),
});

Or with a plain private key (not recommended):

import { AxonClient } from '@axonfi/sdk';
 
const axon = new AxonClient({
  botPrivateKey: process.env.AXON_BOT_PRIVATE_KEY as `0x${string}`,
  vaultAddress: process.env.AXON_VAULT_ADDRESS as `0x${string}`,
  chainId: Number(process.env.AXON_CHAIN_ID),
});

AxonClient derives the bot's public address from the private key and uses it to sign every payment intent. The key never leaves your process.

The encrypted keystore is a recommended safeguard against accidental key exposure, not a hard requirement. Bot keys already have limited blast radius in Axon — they can only sign expiring intents within your spending limits, and you can remove a compromised bot instantly. See Security Model for details.

4. Send Your First Payment

import { encodeRef } from '@axonfi/sdk';
 
const result = await axon.pay({
  to: '0xRecipientAddress',
  token: 'USDC',
  amount: 1,                              // 1 USDC — SDK handles decimals
  ref: encodeRef('quickstart-001'),       // bytes32 reference stored on-chain
  memo: 'My first Axon payment',          // off-chain memo stored by relayer
});
 
console.log('Request ID:', result.requestId);
console.log('Status:', result.status);

Understanding the Response

The pay() method returns one of three shapes depending on the payment path:

// Fast path — synchronous on-chain confirmation
{
  requestId: "req_abc123",
  status: "approved",
  txHash: "0x...",
}
 
// AI scan path — resolved within ~30 seconds
{
  requestId: "req_abc123",
  status: "pending_review",
  pollUrl: "/v1/payments/req_abc123",
  estimatedResolutionMs: 30000,
}
 
// Human review path — requires owner approval
{
  requestId: "req_abc123",
  status: "pending_review",
  pollUrl: "/v1/payments/req_abc123",
}

For a 1 USDC testnet payment, you will almost always hit the fast path and get a txHash immediately.

5. Check Payment Status

If the response status is pending_review, poll for resolution:

const status = await axon.poll(result.requestId);
 
console.log('Status:', status.status); // "approved" | "pending_review" | "rejected"
console.log('Tx Hash:', status.txHash); // populated once confirmed on-chain
console.log('Reason:', status.reason); // populated if rejected

Or use the raw HTTP endpoint:

curl https://relay.axonfi.xyz/v1/payments/req_abc123

6. Full Working Example

import { AxonClient, Chain, encodeRef } from '@axonfi/sdk';
 
async function main() {
  const axon = new AxonClient({
    botPrivateKey: process.env.AXON_BOT_PRIVATE_KEY as `0x${string}`,
    vaultAddress: process.env.AXON_VAULT_ADDRESS as `0x${string}`,
    chainId: Chain.BaseSepolia,
  });
 
  const result = await axon.pay({
    to: '0xRecipientAddress',
    token: 'USDC',
    amount: 1,
    ref: encodeRef('quickstart-001'),
    memo: 'My first Axon payment',
  });
 
  if (result.status === 'approved') {
    console.log('Payment confirmed:', result.txHash);
    return;
  }
 
  // Poll until resolved
  let status = await axon.poll(result.requestId);
  while (status.status === 'pending_review') {
    await new Promise((r) => setTimeout(r, 5000));
    status = await axon.poll(result.requestId);
  }
 
  if (status.status === 'approved') {
    console.log('Payment confirmed:', status.txHash);
  } else {
    console.error('Payment rejected:', status.reason);
  }
}
 
main();

Next Steps