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/sdkThe 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=84532Axon 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 rejectedOr use the raw HTTP endpoint:
curl https://relay.axonfi.xyz/v1/payments/req_abc1236. 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
- How It Works — understand the full payment flow under the hood
- Key Concepts — learn the terminology (owners, vaults, intents, policies)
- Deploy a Vault — set up your own vault on mainnet
- HTTP 402 Payments — integrate with pay-per-request APIs