π 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::req:`}
+
+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 +
π 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:
+-
+
- Install the Freighter extension from
freighter.appor the Chrome/Firefox Web Store
+ - Create or import a Stellar account (12 or 24-word mnemonic) +
- Switch to Testnet in Freighter settings during development +
- 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.
--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.
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:
+-
+
- Ensure
SUPABASE_SERVICE_ROLE_KEYandOPENAI_API_KEY(or your model provider key) are set
+ - Create your agent via UI or API +
- Test the run endpoint β first call should return
402
+ - Verify payment flow with a testnet wallet +
- Check dashboard analytics to confirm billing data populates +
- Set
isPublic: trueto list on the marketplace
+
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 Case | Price (XLM) | Est. USD |
|---|---|---|
| Simple lookup / classification | 0.01 β 0.05 | $0.001 β $0.005 |
| Standard LLM completion | 0.05 β 0.20 | $0.005 β $0.02 |
| Complex analysis / long context | 0.20 β 1.00 | $0.02 β $0.10 |
| Premium / specialized model | 1.00 β 5.00 | $0.10 β $0.50 |
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.
+ +βοΈ How are payments verified on Stellar?+
+Payment verification is performed server-side by querying the Stellar Horizon API. The verifier checks five conditions to accept a payment as valid.
-```ts -async function runPaidAgent(agentId: string, input: string, wallet: { +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;
-}) {
- const url = `/api/agents/${agentId}/run`;
+}
- let res = await fetch(url, {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ input }),
- });
+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) {
- 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') || '';
+ 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: {
- 'Content-Type': 'application/json',
+ ...baseHeaders,
'X-Payment-Tx-Hash': txHash,
- 'X-Payment-Wallet': wallet.address,
+ 'X-Payment-Wallet': wallet.address,
},
- body: JSON.stringify({ input }),
+ 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
npm run cli -- agents list
-# run with 0x402 payment flow from terminal
-npm run cli -- agents run -i "Find arbitrage route" --secret
+# Run an agent with automatic 402 payment handling
+# --secret accepts a raw Stellar secret key for non-interactive payment
+npm run cli -- agents run \
+ -i "Find arbitrage route on XLM/USDC" \
+ --secret SXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
-# check ledger confirmation
+# Create a new agent
+npm run cli -- agents create \
+ --name "My Agent" \
+ --model gpt-4o \
+ --price 0.05 \
+ --system "You are a helpful assistant"
+
+# Inspect an agent's details
+npm run cli -- agents get `}
+
+Transaction commands:
+{`# Check if a transaction is confirmed on Stellar
npm run cli -- tx status
-# inspect transaction details
+# Inspect full transaction details (ops, memo, fees, timing)
npm run cli -- tx inspect
-# a2a routed call
-npm run cli -- a2a call -i "Delegate this task"
-```
+# List recent transactions for a wallet
+npm run cli -- tx history --wallet GCALLER...`}
-## Required Runtime Variables
+A2A commands:
+{`# Route a call from one agent to another
+npm run cli -- a2a call \
+ -i "Delegate: summarize this on-chain report" \
+ --secret SXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
-| Key | Purpose |
-| --- | --- |
-| NEXT_PUBLIC_HORIZON_URL | Horizon endpoint for tx verification |
-| NEXT_PUBLIC_STELLAR_NETWORK | testnet or mainnet explorer mode |
-| QSTASH_URL | Upstash region endpoint |
-| QSTASH_TOKEN | QStash publish auth |
-| QSTASH_CURRENT_SIGNING_KEY | webhook signature verification |
-| QSTASH_NEXT_SIGNING_KEY | key rotation support |
-| SUPABASE_SERVICE_ROLE_KEY | write request logs and billing |
+# View A2A event log
+npm run cli -- a2a log`}
-## Real-Time Observability
+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.
-## Deployment Checklist +Topic map:
+| Topic | Triggered by |
|---|---|
| agentforge.payment.pending | 402 challenge issued to client |
| agentforge.payment.confirmed | On-chain tx verified by Horizon |
| agentforge.agent.completed | Agent run finished (paid or free) |
| agentforge.billing.updated | Earnings aggregated for owner wallet |
| agentforge.marketplace.activity | Public agent interactions (live feed) |
| agentforge.chain.synced | Periodic Horizon sync checkpoint |
| agentforge.a2a.request | A2A routing request dispatched |
| agentforge.a2a.response | A2A 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,
+});`}
+
+ngrok or cloudflared tunnel to expose localhost:3000.