Integration Guides

Quick Start

Get up and running with BillMyAgent in minutes. This guide walks you through creating your first payment. For a guided, language-switchable version see the Quickstart.

Step 1: Get Your API Key

First, you'll need to obtain an API key. Visit our Get API Key page for detailed instructions. This key authenticates all your API requests.

Step 2: Install the SDK

Choose the SDK for your framework or language:

React
Hook + <PaymentButton>
npm install @billmyagent/payments-react
Vue
Composable + button
npm install @billmyagent/payments-vue
Web Components
Framework-free element
npm install @billmyagent/payments-elements
Core
Any JS / TS client
npm install @billmyagent/payments-core
Python
Server / agent
pip install billmyagent

Step 3: Initialize the SDK

To pay x402-gated APIs, build a client with a signer. On a server or agent, create one from a private key (it never leaves your process); in a browser, pass a viem wallet client backed by the user's wallet instead.

JavaScript
import { PaymentClient, createSigner } from '@billmyagent/payments-core';
// Server / agentconst signer = await createSigner('base', process.env.PRIVATE_KEY);const client = new PaymentClient({ signer });

Step 4: Pay an x402-gated API

client.http is an axios instance. Any HTTP 402 it hits is paid automatically (the SDK signs an EIP-3009 transferWithAuthorization and the facilitator settles it on-chain) and the request is retried β€” no manual handling.

JavaScript
const res = await client.http.get('https://api.example.com/paid-resource');
console.log(res.data, 'paid:', Boolean(res.headers['x-payment-response']));

Merchant REST API

To manage your own account (record payments, payout addresses, fees, invoices), pass an apiKey (and an authToken JWT for /merchant/*) and call the client methods directly:

JavaScript
const client = new PaymentClient({ apiKey: 'your-api-key', authToken: jwt });
await client.getPayoutSettings();await client.updatePayoutSettings({ base_address: '0x…' });await client.listPayments();

React Integration

For React applications, use the provided PaymentButton component or the usePayment hook:

Using PaymentButton Component

JSX
import { PaymentButton, PaymentClient, createSigner } from '@billmyagent/payments-react';
// In a browser, build the signer from the user's wallet (e.g. window.ethereum).const client = new PaymentClient({ signer: await createSigner('base', PRIVATE_KEY) });
function App() {  return (    <PaymentButton      client={client}      url="https://api.example.com/premium-article"      onSuccess={(data) => console.log('unlocked', data)}      onError={(error) => console.error('payment failed', error)}    >      Read article    </PaymentButton>  );}

Using the usePayment Hook

JSX
import { usePayment } from '@billmyagent/payments-react';
function PremiumContent({ client }) {  const { pay, data, loading, error, paid } = usePayment(client);
  return (    <div>      <button onClick={() => pay('https://api.example.com/premium')} disabled={loading}>        {loading ? 'Loading…' : 'Load premium'}      </button>      {error && <p>Error: {error.message}</p>}      {data && <p>Loaded{paid ? ' (paid)' : ''}</p>}    </div>  );}

Vue Integration

For Vue applications, use the PaymentButton component:

HTML
<template>  <PaymentButton    :client="client"    url="https://api.example.com/premium-article"    label="Read article"    :onSuccess="handleSuccess"    :onError="handleError"  /></template>
<script setup>import { PaymentButton, PaymentClient, createSigner } from '@billmyagent/payments-vue';
const client = new PaymentClient({ signer: await createSigner('base', PRIVATE_KEY) });
const handleSuccess = (data) => console.log('unlocked', data);const handleError = (error) => console.error('payment failed', error);</script>

How HTTP 402 Payment Required Is Handled

When an API endpoint requires payment, it returns HTTP 402 with the x402 payment requirements. A client built with a signer handles this automatically β€” there is no separate adapter to configure:

JavaScript
const client = new PaymentClient({  signer: await createSigner('base', process.env.PRIVATE_KEY),});
// When the API returns 402, client.http automatically:// 1. reads the x402 payment requirements (the `accepts` challenge)// 2. signs an EIP-3009 transferWithAuthorization with your wallet// 3. retries the request with the X-PAYMENT header// 4. the facilitator settles it on-chain
const res = await client.http.get('https://api.example.com/premium');if (res.headers['x-payment-response']) {  console.log('paid and unlocked:', res.data);}

Charge for Your API (Seller Side)

The SDKs above pay x402 paywalls. To charge β€” put your own endpoint behind a 402 β€” use the official x402 middleware. A request gets 402 Payment Required with machine-readable payment instructions until a valid payment is presented; the facilitator settles it and your payout address receives the funds directly (non-custodial). Runnable examples live in examples/seller-express and examples/seller-next.

Express

JavaScript
import express from 'express';import { paymentMiddleware } from 'x402-express';
const app = express();
// PAY_TO is your payout wallet (the same address you set in Payouts).app.use(  paymentMiddleware(process.env.PAY_TO, {    'GET /premium': { price: '$0.01', network: 'base' },  }));
app.get('/premium', (_req, res) => res.json({ unlocked: true }));app.listen(4021);

Next.js (App Router)

TypeScript
// middleware.tsimport { paymentMiddleware } from 'x402-next';
export const middleware = paymentMiddleware(process.env.PAY_TO, {  '/premium': { price: '$0.01', network: 'base' },});
export const config = { matcher: ['/premium/:path*'] };

Networks are limited to those the facilitator can settle: base, base-sepolia (for testing), and polygon. Mainnet settlement requires a facilitator with CDP credentials; the public x402.org facilitator is testnet-only.

When you want a buyer to pay without building any checkout UI, create a payment intent and share the hosted checkout_url it returns. The buyer opens the link, sees your merchant branding, connects a wallet, and pays USDC on-chain. Because BillMyAgent is strictly non-custodial, funds settle directly to your payout wallet β€” the platform never holds them. The 1% platform fee (0.5% at volume) is recorded and billed monthly out-of-band; you keep 100% on-chain. Links are single-use and expire (default 7 days, max 30). Settlement is on base and polygon (USDC), plus base-sepolia for testing; Ethereum is not currently supported for settlement.

Prerequisite: configure a payout address for the intent's network (Payouts page or PUT /merchant/payout-settings), or intent creation returns 422 no_payout_address.

Step 1: Create the intent and share the link

The SDK (v2.2.0) exposes createPaymentIntent, getPaymentIntent, listPaymentIntents, cancelPaymentIntent, and a getCheckoutUrl(id) helper. Python uses the snake_case equivalents plus checkout_url(id).

JavaScript
import { PaymentClient } from '@billmyagent/payments-core';
const client = new PaymentClient({ apiKey: process.env.BILLMYAGENT_API_KEY });
const intent = await client.createPaymentIntent({  amount: '25.00',       // decimal string, [0.01, 1000000]  network: 'base',       // 'base' | 'base-sepolia' | 'polygon'  currency: 'USDC',      // optional, defaults to USDC  description: 'Pro plan β€” March',  expires_in_seconds: 604800, // optional; default 7 days, max 30 days});
// Share this hosted link with your buyer:console.log(intent.checkout_url); // or client.getCheckoutUrl(intent.id)

Step 2: Poll for settlement

The intent moves requires_payment β†’ processing β†’ settled. Poll getPaymentIntent (or listen for webhooks) until it is settled; watch for expired and canceled too.

JavaScript
async function waitForSettlement(id) {  for (;;) {    const intent = await client.getPaymentIntent(id);    if (intent.status === 'settled') return intent; // funds are in your payout wallet    if (intent.status === 'expired' || intent.status === 'canceled') {      throw new Error(`intent ${intent.status}`);    }    await new Promise((r) => setTimeout(r, 5000));  }}
Python
import timefrom billmyagent import PaymentClient
client = PaymentClient(api_key=os.environ['BILLMYAGENT_API_KEY'])
intent = client.create_payment_intent(    amount='25.00', network='base', description='Pro plan β€” March')print(intent['checkout_url'])  # or client.checkout_url(intent['id'])
while True:    current = client.get_payment_intent(intent['id'])    if current['status'] == 'settled':        break    if current['status'] in ('expired', 'canceled'):        raise RuntimeError(current['status'])    time.sleep(5)

Refunds: payment links are non-refundable through the platform. If a buyer needs a refund, handle it directly (merchant↔buyer) off-platform. To stop an unpaid link early, call cancelPaymentIntent(id).

Webhook Integration

To receive payment events, set up a webhook endpoint in your application and register it with BillMyAgent. Webhooks will be sent for events like:

  • Payment created
  • Payment verified
  • Payment completed
  • Payment failed
  • HTTP 402 responses (when payment is required)

Next Steps

Check out our Code Examples for more detailed implementation examples, or build requests interactively in the API Explorer.