Home/Developer Hub
Developer Hub

Production-first integration reference for building on the 0x402 protocol โ€” real Stellar payments, QStash event routing, and AI agent orchestration.

๐ŸŒ What is 0x402 Protocol?+

The 0x402 Protocol is an open standard for machine-to-machine micropayments built on top of the HTTP specification. It extends the dormant 402 Payment Required status code into a fully functional request-payment-retry handshake, enabling any API endpoint to monetize its responses in real time.

In the AgentForge implementation, 0x402 is powered by the Stellar blockchain. When a client calls a paid agent endpoint and the server detects no valid payment credential, it responds with:

HTTP/1.1 402 Payment Required
X-Payment-Required: xlm
X-Payment-Amount: 0.05
X-Payment-Address: GXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
X-Payment-Network: stellar
X-Payment-Memo: agent:<id>:req:<nonce>

The client then creates and submits a real XLM transaction to the Stellar network, captures the transaction hash, and retries the original request with the payment proof in headers. The server verifies the transaction on-chain via Horizon before fulfilling the request.

This creates a trustless, permissionless payment layer where:

  • No API keys or billing accounts are needed
  • Payments settle in 3โ€“5 seconds on Stellar
  • The payment proof is permanently auditable on-chain
  • Any wallet can pay any agent without prior registration
๐Ÿ’ก Tip: The minimum denomination is 0.0000001 XLM (1 stroop), making sub-cent micropayments economically viable for high-frequency agent calls.
๐Ÿ” How do I connect my Freighter wallet?+

Freighter is a browser extension wallet for the Stellar network, analogous to MetaMask for Ethereum. It is the primary wallet integration in AgentForge for user-facing payment flows.

Installation:

  1. Install the Freighter extension from freighter.app or the Chrome/Firefox Web Store
  2. Create or import a Stellar account (12 or 24-word mnemonic)
  3. Switch to Testnet in Freighter settings during development
  4. Fund your testnet account using https://friendbot.stellar.org/?addr=YOUR_ADDRESS

Integration in your app:

import { isConnected, getPublicKey, signTransaction } from '@stellar/freighter-api';

// Check if Freighter is installed
const connected = await isConnected();
if (!connected) throw new Error('Freighter not installed');

// Request the user's public key
const publicKey = await getPublicKey();

// Sign and submit a payment transaction
const signedXDR = await signTransaction(unsignedXDR, {
network: 'TESTNET',
networkPassphrase: 'Test SDF Network ; September 2015',
});

How AgentForge uses Freighter: When an agent endpoint returns a 402, the WalletConnect component triggers the payXLM helper which builds a Stellar payment transaction, presents it to Freighter for signing, and submits the signed XDR to Horizon. The resulting transaction hash is forwarded in the retry request header.

โš ๏ธ Note: Freighter requires the user to approve each transaction. For non-interactive server-side flows (CLI or A2A), use a raw Stellar secret key passed via --secret flag.
๐Ÿ’ณ How does the 402 Payment flow work?+

The full end-to-end payment flow for a paid agent call involves three phases: probe โ†’ pay โ†’ prove.

Phase 1 โ€” Probe (initial request):

POST /api/agents/:id/run
Content-Type: application/json

{ "input": "Analyze this on-chain orderbook spread" }

If the agent requires payment, the server returns a 402 with payment headers. No computation is performed yet.

Phase 2 โ€” Pay (wallet transaction):

// Using Freighter or a raw keypair:
const txHash = await wallet.payXLM(
paymentAddress,  // from X-Payment-Address header
paymentAmount,   // from X-Payment-Amount header  
paymentMemo      // from X-Payment-Memo header (CRITICAL โ€” links tx to request)
);

The memo field is the most important part โ€” it binds the on-chain transaction to a specific agent request nonce, preventing replay attacks.

Phase 3 โ€” Prove (retry with proof):

POST /api/agents/:id/run
Content-Type: application/json
X-Payment-Tx-Hash: 4e7a2b9c...
X-Payment-Wallet: GCALLER...

{ "input": "Analyze this on-chain orderbook spread" }

The server fetches the transaction from Horizon, verifies:

  • The transaction hash is valid and confirmed on the correct network
  • The destination address matches the agent owner's wallet
  • The amount is โ‰ฅ the required price
  • The memo matches the expected pattern agent:<id>:req:<nonce>
  • The transaction has not been used before (replay protection via Supabase)

On success, the agent runs, results are returned, and a QStash agentforge.payment.confirmed event is published for billing and marketplace tracking.

๐Ÿ’ก The entire flow is implemented in lib/paymentVerifier.ts and the agent route handler at app/api/agents/[id]/run/route.ts.
๐Ÿค– How do I create and deploy an agent?+

Agents are created via the AgentBuilder UI or directly via the POST /api/agents endpoint. Each agent is owned by a Stellar wallet address and stored in Supabase.

Create via API:

POST /api/agents
Content-Type: application/json
X-Wallet-Address: GOWNER...

{
"name": "MEV Scanner",
"description": "Scans Stellar DEX for arbitrage opportunities",
"model": "gpt-4o",
"systemPrompt": "You are a DeFi analyst specializing in Stellar AMM pools...",
"price": "0.05",
"isPublic": true,
"tags": ["defi", "stellar", "arbitrage"]
}

Response:

{
"id": "agt_7f3k2...",
"name": "MEV Scanner",
"owner": "GOWNER...",
"price": "0.05",
"model": "gpt-4o",
"isPublic": true,
"createdAt": "2024-01-15T10:23:00Z",
"runUrl": "/api/agents/agt_7f3k2.../run"
}

Deploy checklist:

  1. Ensure SUPABASE_SERVICE_ROLE_KEY and OPENAI_API_KEY (or your model provider key) are set
  2. Create your agent via UI or API
  3. Test the run endpoint โ€” first call should return 402
  4. Verify payment flow with a testnet wallet
  5. Check dashboard analytics to confirm billing data populates
  6. Set isPublic: true to list on the marketplace
โš ๏ธ Free agents: Set price: "0" to create a free agent. The 402 flow is skipped entirely โ€” requests are served immediately without payment verification.
๐Ÿ’ฐ What is the minimum price per request?+

The minimum price for a paid agent request is 0.0000001 XLM (1 stroop), the smallest denomination on Stellar. In practice, meaningful pricing starts at around 0.01 XLM (~$0.001) per call.

Recommended pricing tiers:

Use CasePrice (XLM)Est. USD
Simple lookup / classification0.01 โ€“ 0.05$0.001 โ€“ $0.005
Standard LLM completion0.05 โ€“ 0.20$0.005 โ€“ $0.02
Complex analysis / long context0.20 โ€“ 1.00$0.02 โ€“ $0.10
Premium / specialized model1.00 โ€“ 5.00$0.10 โ€“ $0.50
๐Ÿณ Local Runtime & Docker Runner+

Run agents locally for development using the CLI or an isolated Docker runner. This reproduces server behavior without needing to call the hosted API.

# Direct (fast) โ€” calls local consumers.executeAgentLocally
npx ts-node --project tsconfig.json cli/index.ts runtime run agent-1 -i "Analyze market"

# Use Docker runner (build image first)
docker build -t agentforge-runner:dev -f runtime/runner/Dockerfile .

# Run inside container (mount workspace)
docker run --rm -v "$PWD":/workspace -w /workspace agentforge-runner:dev node -e "require('child_process').execSync('npx ts-node --project tsconfig.json runtime/runner/index.ts agent-1 "Analyze market"',{stdio: 'inherit'})"

# Convenience: CLI flag
npx ts-node --project tsconfig.json cli/index.ts runtime run agent-1 -i "..." --docker

Stellar transaction fees are fixed at 100 stroops (0.00001 XLM) per transaction โ€” negligible compared to any reasonable pricing. There are no protocol-level platform fees on AgentForge; the full amount goes directly to the agent owner's wallet.

๐Ÿ’ก For agents that handle batched or long-running tasks, consider implementing your own rate limiting and bundling multiple agent calls into a single higher-value payment to amortize the per-transaction verification overhead.
โ›“๏ธ How are payments verified on Stellar?+

Note: The Docker runner image is intended for development and prototyping. For production, compile the runner to JS and build a slimmer runtime image.

Payment verification is performed server-side by querying the Stellar Horizon API. The verifier checks five conditions to accept a payment as valid.

Verification logic (simplified):

async function verifyPayment(txHash: string, agentId: string, expectedAmount: string, ownerAddress: string) {
// 1. Fetch transaction from Horizon
const tx = await horizon.transactions().transaction(txHash).call();

// 2. Ensure it's confirmed (not pending)
if (!tx.successful) throw new Error('Transaction not confirmed');

// 3. Parse operations โ€” find the payment op
const ops = await tx.operations().call();
const paymentOp = ops.records.find(op => op.type === 'payment');

// 4. Validate destination, amount, asset
if (paymentOp.to !== ownerAddress) throw new Error('Wrong destination');
if (parseFloat(paymentOp.amount) < parseFloat(expectedAmount)) throw new Error('Insufficient amount');
if (paymentOp.asset_type !== 'native') throw new Error('Non-XLM payment');

// 5. Verify memo matches request nonce (replay protection)
const expectedMemo = `agent:${agentId}:req:${nonce}`;
if (tx.memo !== expectedMemo) throw new Error('Memo mismatch');

// 6. Check not already used (idempotency)
const used = await db.requestLogs.findFirst({ where: { txHash } });
if (used) throw new Error('Transaction already redeemed');

return { verified: true, txHash, explorerUrl: `https://stellar.expert/explorer/testnet/tx/${txHash}` };
}

The Horizon endpoint is configured via NEXT_PUBLIC_HORIZON_URL. For testnet: https://horizon-testnet.stellar.org. For mainnet: https://horizon.stellar.org.

Explorer links are stored alongside each verified request in Supabase, surfaced in the dashboard invoice table and returned in the run API response body.

๐Ÿ”Œ How do I integrate the API in my app?+

The complete JavaScript/TypeScript integration pattern handles the full 402 handshake in a single reusable function:

// lib/agentClient.ts

export interface AgentWallet {
address: string;
payXLM: (to: string, amount: string, memo: string) => Promise<string>;
}

export async function runAgent(
agentId: string,
input: string,
wallet?: AgentWallet
): Promise<{ output: string; txHash?: string; explorerUrl?: string }> {
const url = `/api/agents/${agentId}/run`;
const body = JSON.stringify({ input });
const baseHeaders = { 'Content-Type': 'application/json' };

// Initial probe request
let res = await fetch(url, { method: 'POST', headers: baseHeaders, body });

// Handle 402 payment challenge
if (res.status === 402) {
  if (!wallet) throw new Error('This agent requires payment โ€” provide a wallet');

  const amount  = res.headers.get('X-Payment-Amount')  ?? '0';
  const address = res.headers.get('X-Payment-Address') ?? '';
  const memo    = res.headers.get('X-Payment-Memo')    ?? '';

  // Submit payment and get transaction hash
  const txHash = await wallet.payXLM(address, amount, memo);

  // Retry with payment proof
  res = await fetch(url, {
    method: 'POST',
    headers: {
      ...baseHeaders,
      'X-Payment-Tx-Hash': txHash,
      'X-Payment-Wallet':  wallet.address,
    },
    body,
  });
}

if (!res.ok) {
  const err = await res.json().catch(() => ({}));
  throw new Error(err.error ?? `Agent error: ${res.status}`);
}

return res.json();
}

Usage with Freighter:

import { getPublicKey, signTransaction } from '@stellar/freighter-api';
import { TransactionBuilder, Networks, Asset, Operation, Server } from '@stellar/stellar-sdk';

const wallet: AgentWallet = {
address: await getPublicKey(),
payXLM: async (to, amount, memo) => {
  const server = new Server(process.env.NEXT_PUBLIC_HORIZON_URL!);
  const account = await server.loadAccount(wallet.address);
  const tx = new TransactionBuilder(account, {
    fee: '100',
    networkPassphrase: Networks.TESTNET,
  })
    .addOperation(Operation.payment({ destination: to, asset: Asset.native(), amount }))
    .addMemo(Memo.text(memo))
    .setTimeout(30)
    .build();

  const signedXDR = await signTransaction(tx.toXDR(), { network: 'TESTNET' });
  const result = await server.submitTransaction(TransactionBuilder.fromXDR(signedXDR, Networks.TESTNET));
  return result.hash;
},
};

const result = await runAgent('agt_7f3k2...', 'Analyze this pool', wallet);
console.log(result.output);
โŒจ๏ธ How do I use the CLI?+

The AgentForge CLI (cli/ directory) provides a terminal interface for all platform operations, including agent management, transaction inspection, and A2A routing โ€” without needing the browser UI.

Agent commands:

# List all available agents
pnpm run cli -- agents list

# Run an agent with automatic 402 payment handling
# --secret accepts a raw Stellar secret key for non-interactive payment
pnpm run cli -- agents run <agent-id> -i "Find arbitrage route on XLM/USDC" --secret SXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

# Create/Build a new agent directly on-chain via CLI
pnpm run cli -- agents build --name "Soroswap Arbitrage Agent" --prompt "You are an automated DeFi trading bot. Monitor constant-product AMM price divergence on Soroswap finance and Aquarius pools. Execute BUY when XLM is undervalued." --desc "Real-time liquidity arbitrage strategy bot" --model "openai-gpt4o-mini" --price 0.05

# Inspect an agent's details
pnpm run cli -- agents get <agent-id>

Agent Smart Wallet & Portfolio commands:

# Check portfolio balances of Owner Wallet and Agent Smart Wallet side-by-side
pnpm run cli -- wallet agent-balance

# Deposit funds (XLM or AF$) from Owner Wallet to Agent Smart Wallet
pnpm run cli -- wallet agent-deposit 10 --currency af

# Withdraw funds (XLM or AF$) from Agent Smart Wallet back to Owner Wallet
pnpm run cli -- wallet agent-withdraw 5 --currency af

# Check native Stellar balance of any key on mainnet
pnpm run cli -- wallet balance GARN7A6OJKPR3HAPVIKM6GRUD7KMEHYQ76VJJCO4AAKQ6ETEKFQPQ24T --network mainnet

Paper Trading Simulator commands:

# Execute a simulated paper trade agent strategy run (fetches live Horizon/Soroswap pricing)
pnpm run cli -- papertrade run 30cbc1b8-9ba7-4d99-9e25-484c7d86ca33 -i "USDC price trend is reversing, optimize XLM accumulation"

# View paper trading history/ledger
pnpm run cli -- papertrade history

# Display paper trading balances
pnpm run cli -- papertrade balance

# Display active positions and total equity valuation
pnpm run cli -- papertrade position

# Reset paper trading ledger
pnpm run cli -- papertrade reset

PRoot Sandboxed Local Runtime:

# Run agent workflow securely sandboxed inside PRoot virtual box
pnpm run cli -- runtime run 8e460885-8b34-4419-8ca8-bb396f3b2557 -i "Analyze Soroswap pricing dynamics" --proot
pnpm run cli -- runtime run 8e460885-8b34-4419-8ca8-bb396f3b2557 -i "Analyze this workflow log file" --proot

Transaction commands:

# Check if a transaction is confirmed on Stellar
pnpm run cli -- tx status <tx_hash>

# Inspect full transaction details (ops, memo, fees, timing)
pnpm run cli -- tx inspect <tx_hash>

# List recent transactions for a wallet
pnpm run cli -- tx history --wallet GCALLER...

A2A commands:

# Route a call from one agent to another
pnpm run cli -- a2a call <from-agent-id> <to-agent-id> -i "Delegate: summarize this on-chain report" --secret SXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

# View A2A event log
pnpm run cli -- a2a log

The CLI reads NEXT_PUBLIC_HORIZON_URL and NEXT_PUBLIC_STELLAR_NETWORK from the environment, so make sure a .env.local file is present or variables are exported before running commands.

๐Ÿ“จ What are QStash topics and consumers?+

QStash (by Upstash) is the event backbone of AgentForge. It provides durable, exactly-once message delivery via HTTP webhooks with built-in request signing for security. Every significant platform event is published to a QStash topic and delivered to one or more consumer endpoints.

Topic map:

TopicTriggered by
agentforge.payment.pending402 challenge issued to client
agentforge.payment.confirmedOn-chain tx verified by Horizon
agentforge.agent.completedAgent run finished (paid or free)
agentforge.billing.updatedEarnings aggregated for owner wallet
agentforge.marketplace.activityPublic agent interactions (live feed)
agentforge.chain.syncedPeriodic Horizon sync checkpoint
agentforge.a2a.requestA2A routing request dispatched
agentforge.a2a.responseA2A target agent response received

Consumer endpoints (app/api/consumers/) receive QStash deliveries, verify the Upstash-Signature header using QSTASH_CURRENT_SIGNING_KEY / QSTASH_NEXT_SIGNING_KEY, then update Supabase records and broadcast to Ably channels.

// Signature verification pattern (all consumer endpoints)
import { Receiver } from '@upstash/qstash';

const receiver = new Receiver({
currentSigningKey: process.env.QSTASH_CURRENT_SIGNING_KEY!,
nextSigningKey:    process.env.QSTASH_NEXT_SIGNING_KEY!,
});

await receiver.verify({
signature: req.headers.get('Upstash-Signature') ?? '',
body:      rawBody,
});
โš ๏ธ Note: Consumer endpoints must be publicly reachable for QStash delivery. In local development, use ngrok or cloudflared tunnel to expose localhost:3000.
๐Ÿ“Š How do I monitor my agent's performance?+

AgentForge provides real-time observability through two surfaces: the Dashboard (browser) and the Analytics API (programmatic).

Dashboard features:

  • Candlestick chart of request volume (5-second polling via /api/dashboard/analytics)
  • Earnings breakdown by model and time window
  • Invoice table with Stellar Explorer links for every paid request
  • Live activity feed via Ably WebSocket channel (agentforge-live)
  • Per-agent request history with latency, status, and tx hash

Analytics API:

GET /api/dashboard/analytics?owner=GWALLET...&hours=24

// Response shape:
{
"byModel": [
  { "model": "gpt-4o", "paidCount": 142, "freeCount": 38, "earnings": "7.10" }
],
"requestRate": [
  { "minute": "2024-01-15T10:00:00Z", "count": 12 }
],
"earnings": [
  { "date": "2024-01-15", "total": "7.10" }
],
"invoices": [
  {
    "txHash": "4e7a2b9c...",
    "amount": "0.05",
    "agentId": "agt_7f3k2...",
    "timestamp": "2024-01-15T10:23:04Z",
    "explorerUrl": "https://stellar.expert/explorer/testnet/tx/4e7..."
  }
]
}

All analytics data is sourced from live Supabase records โ€” no static mocks. The hours parameter accepts 1, 6, 24, 72, or 168 (7 days).

๐Ÿง  What AI models are supported?+

AgentForge supports any model exposed via an OpenAI-compatible API. The model is specified per-agent and passed through to the completion provider on each run.

Supported providers and models:

ProviderModelsEnv Key Required
OpenAIgpt-4o, gpt-4o-mini, gpt-4-turbo, gpt-3.5-turboOPENAI_API_KEY
Anthropicclaude-3-5-sonnet, claude-3-haikuANTHROPIC_API_KEY
Googlegemini-1.5-pro, gemini-1.5-flashGOOGLE_API_KEY
Groqllama-3.1-70b, mixtral-8x7bGROQ_API_KEY
Local / CustomAny OpenAI-compatible endpointCUSTOM_LLM_BASE_URL

The model selection is stored in the agent record and resolved at run time. Dashboard analytics break down earnings and request counts per model, so you can track the cost and revenue profile of each model tier you offer.

๐Ÿ’ก For cost-optimized agents, use gpt-4o-mini or gemini-1.5-flash at lower price points. For premium agents requiring complex reasoning, use gpt-4o or claude-3-5-sonnet at higher price points to maintain margin.
๐Ÿ”€ How does A2A (Agent-to-Agent) routing work?+

Agent-to-Agent (A2A) routing enables one agent to call another as part of its execution โ€” effectively creating multi-hop AI pipelines where each hop may have its own payment requirement.

A2A flow:

  1. Agent A receives a request that requires sub-task delegation
  2. Agent A calls POST /api/a2a/route with the target agent ID and sub-task input
  3. If the target agent (B) is paid, Agent A's wallet pays Agent B's owner
  4. The A2A request is published to agentforge.a2a.request QStash topic
  5. The consumer delivers the request to Agent B's run endpoint
  6. Agent B's response is published to agentforge.a2a.response
  7. Agent A's execution resumes with the delegated result

A2A API:

POST /api/a2a/route
Content-Type: application/json
X-Payment-Wallet: GCALLER_AGENT...
X-Payment-Tx-Hash: <tx_hash_if_target_is_paid>

{
"fromAgentId": "agt_caller...",
"toAgentId":   "agt_target...",
"input":       "Summarize the MEV opportunity found in this orderbook data: ..."
}

CLI equivalent:

npm run cli -- a2a call agt_caller... agt_target... -i "Delegate: analyze this pool liquidity" --secret SXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

A2A identity: The X-Payment-Wallet header in an A2A call identifies the calling agent's owner wallet. This creates an on-chain audit trail linking caller โ†’ callee wallet addresses, allowing billing to be correctly attributed across the agent graph.

โš ๏ธ Circular routing prevention: A2A calls track the call chain depth. Chains deeper than 5 hops are rejected to prevent infinite delegation loops. This limit is configurable via the A2A_MAX_DEPTH environment variable.
Environment Variables
VariablePurpose
NEXT_PUBLIC_HORIZON_URLHorizon endpoint for tx verification
NEXT_PUBLIC_STELLAR_NETWORKtestnet or mainnet
QSTASH_URLUpstash regional endpoint
QSTASH_TOKENQStash publish auth token
QSTASH_CURRENT_SIGNING_KEYWebhook signature verification
QSTASH_NEXT_SIGNING_KEYKey rotation support
SUPABASE_SERVICE_ROLE_KEYWrite request logs and billing records
ABLY_API_KEYLive activity broadcast to dashboard
OPENAI_API_KEYDefault LLM provider