Quickstart: Headless Agent

15 minutes

Enable autonomous agents to handle pay-as-you-go APIs

Overview

Autonomous Payments
Headless agents can automatically handle HTTP 402 payment challenges, allowing them to access paid APIs while respecting budget constraints.

Step 1: Create Agent Class

class PayAsYouGoAgent {
  private budget: number;
  private spent: number = 0;

  constructor(initialBudget: number) {
    this.budget = initialBudget;
  }

  async call(endpoint: string, data: any, maxPrice: number = 1.0) {
    const idempotencyKey = crypto.randomUUID();

    let response = await fetch(endpoint, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Idempotency-Key': idempotencyKey,
      },
      body: JSON.stringify(data),
    });

    if (response.status === 402) {
      const price = parseFloat(response.headers.get('X-402-Price')!);
      const nonce = response.headers.get('X-402-Nonce');

      // Check constraints
      if (price > maxPrice) {
        throw new Error(`Price ${price} exceeds max ${maxPrice}`);
      }
      if (this.spent + price > this.budget) {
        throw new Error('Insufficient budget');
      }

      // Approve payment
      await this.approvePayment(nonce!, price);

      // Retry with nonce
      response = await fetch(endpoint, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Idempotency-Key': idempotencyKey,
          'X-402-Nonce': nonce!,
        },
        body: JSON.stringify(data),
      });

      this.spent += price;
    }

    return response.json();
  }

  private async approvePayment(nonce: string, amount: number) {
    // In production: submit Solana transaction
    // In test mode: call simulate endpoint
    await fetch('/api/simulate/pay', {
      method: 'POST',
      body: JSON.stringify({ nonce, amount }),
    });
  }
}

Step 2: Use in Your Agent

const agent = new PayAsYouGoAgent(10.0); // $10 budget

// Execute commands with automatic payment handling
const result1 = await agent.call(
  '/api/v1/bots/mybot/commands/analyze',
  { args: { q: 'BTC' }, user_id: 'agent:001' },
  0.10 // max $0.10 per call
);

const result2 = await agent.call(
  '/api/v1/bots/mybot/commands/chart',
  { args: { symbol: 'ETH' }, user_id: 'agent:001' },
  0.10
);

console.log(`Spent: $${agent.getSpent()}`);
console.log(`Remaining: $${agent.getRemainingBudget()}`);

Step 3: Add Budget Management

Budget Controls
Always implement budget limits to prevent unexpected costs in autonomous agents.
// Set budget limits
agent.setBudget(20.0);
agent.setMaxPricePerCall(0.50);

// Track spending
agent.on('payment', (amount, command) => {
  console.log(`Paid ${amount} for ${command}`);
  
  if (agent.getRemainingBudget() < 1.0) {
    console.warn('Low budget warning');
  }
});

// Handle budget exhaustion
try {
  await agent.call(endpoint, data);
} catch (error) {
  if (error.message === 'Insufficient budget') {
    // Request more budget or pause agent
    await requestBudgetIncrease();
  }
}

Step 4: Verify Receipts

Always verify receipt signatures to ensure payment integrity:

import crypto from 'crypto';

function verifyReceipt(receipt: any, signature: string, secret: string): boolean {
  const { signature: _, ...payload } = receipt;
  const message = JSON.stringify(payload, Object.keys(payload).sort());
  
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(message)
    .digest('hex');
  
  return crypto.timingSafeEqual(
    Buffer.from(expectedSignature),
    Buffer.from(signature)
  );
}

// Use after each payment
const result = await agent.call(endpoint, data);
const isValid = verifyReceipt(
  result.receipt,
  result.receipt.signature,
  WEBHOOK_SECRET
);

if (!isValid) {
  throw new Error('Invalid receipt signature');
}

Best Practices

Set reasonable budget limits per agent
Implement per-call price maximums
Log all payments for auditing
Verify all receipt signatures
Handle payment failures gracefully
Monitor agent spending in real-time