Agent payments
Let an AI agent add Layerbeat credit itself over HTTP 402, with x402 on Solana (pay.sh) or MPP on Tempo, from the CLI, MCP or its own code.
An agent can add personal credit to its Layerbeat account without a person paying for it: it asks for a top-up, receives 402 Payment Required, pays from its own wallet and repeats the request. Layerbeat supports two protocols:
| Protocol | Network and token | Clients | Endpoint |
|---|---|---|---|
x402 V2 (exact) | Solana, native USDC | pay (pay curl), x402 SDKs | POST /v1/billing/top-ups/x402 |
MPP (tempo charge) | Tempo, USDC.e | mppx, pympp and other MPP clients | POST /v1/billing/top-ups/mpp |
Both add USD credit to the user who owns the API key. Neither buys anything by itself: the agent then orders a VPS with the API, the CLI or its MCP tools, spending that credit.
Your API key stays in Authorization
Both endpoints need a Layerbeat API key with billing:write. The payment travels in its own header (PAYMENT-SIGNATURE or Payment-Authorization), so the key and the payment never replace each other. Give agents their own key and keep keys and wallet secrets out of prompts and logs.
From start to finish
An agent with an API key (billing:read, billing:write, vm:read, vm:write) and a wallet can go from no credit to a running server:
layerbeat credit --currency USD # 1. what is there
layerbeat topup 10 --pay x402 --yes # 2. add $10 from the agent's Solana wallet
layerbeat quote --region sgp --plan SG-B224 --image "Ubuntu 24.04" # 3. price the server
layerbeat new web-1 --region sgp --plan SG-B224 --image "Ubuntu 24.04" \
--ssh-key agent --max-price 10 --idempotency-key web-1-001 --yes --wait # 4. buy it, never above $10Use --pay mpp to pay with USDC.e on Tempo instead. Through MCP, the same steps are the credit, top_up (with pay), quote and create_server tools, available when the server runs with layerbeat mcp serve --allow-spend.
With the Layerbeat CLI
layerbeat topup <amount> --pay x402 runs pay and --pay mpp runs mppx to pay from this computer's wallet. See Credit.
- The key stays in the CLI. The payer never receives your Layerbeat key: the CLI gives it a one-time address on
127.0.0.1and forwards only this top-up. - The amount is capped. The CLI refuses an offer above the amount, plus the identifying suffix of under one cent.
- It never pays twice. Retrying with the same
--idempotency-keycannot pay again.
With pay.sh
pay wraps curl and pays HTTP 402 challenges from a local wallet. It sends the same request again with your headers, so the API key and idempotency key stay in place:
pay curl -X POST https://layerbeat.com/v1/billing/top-ups/x402 \
-H "Authorization: Bearer $LAYERBEAT_API_KEY" \
-H "Idempotency-Key: agent-topup-001" \
-H "Content-Type: application/json" \
--data '{"amount":"5.00"}'The offered amount is a few micro-USDC above the round figure (for example 5.004213). That suffix identifies your payment and is kept as credit.
From code
Each example pays a $5.00 top-up and reads the API key, a top-up key and the wallet from the environment. TOPUP_KEY identifies one top-up: reuse it for every retry.
// npm install @x402/fetch @x402/svm @solana/kit @scure/base
import { x402Client, wrapFetchWithPayment } from '@x402/fetch'
import { ExactSvmScheme } from '@x402/svm/exact/client'
import { createKeyPairSignerFromBytes } from '@solana/kit'
import { base58 } from '@scure/base'
const signer = await createKeyPairSignerFromBytes(base58.decode(process.env.SVM_PRIVATE_KEY))
const client = new x402Client()
client.setSpendControls({ maxAmountPerPayment: '$5.01' }) // the amount plus the suffix
client.register('solana:*', new ExactSvmScheme(signer))
const fetchWithPayment = wrapFetchWithPayment(fetch, client)
const res = await fetchWithPayment('https://layerbeat.com/v1/billing/top-ups/x402', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.LAYERBEAT_API_KEY}`,
'Idempotency-Key': process.env.TOPUP_KEY,
},
body: JSON.stringify({ amount: '5.00' }),
})
console.log(res.status, (await res.json()).status) // 201 paidA 202 means the payment is still confirming: run the same code again with the same TOPUP_KEY.
How MPP payments are checked
The MPP challenge (WWW-Authenticate: Payment, method="tempo", intent="charge") offers two ways to pay, and both carry mppx's attribution memo for this challenge:
- Push: your wallet broadcasts a
transferWithMemoof the exact amount to the recipient, and the credential carries the transaction hash. - Pull: the credential carries the signed transaction and Layerbeat broadcasts it, once. Layerbeat broadcasts only a transaction that makes exactly that one call on this chain, pays its own fee and, if it expires, expires no later than the challenge.
| Response | Meaning | What to do |
|---|---|---|
402 with a challenge | Payment required | Pay the exact amount of currency to recipient |
201 with Payment-Receipt | A final transfer credited your wallet | Done |
202 | The transaction is recorded but not final | Repeat the request; do not pay again |
503 payment_not_final | Your broadcast transaction is not final yet | Send the same credential again in a few seconds |
402 with error: payment_verification_failed | The transaction does not pay this challenge (amount, token, recipient, memo, or it failed) | Check it. A transfer of the exact amount is still credited by the payment watcher |
Never pay twice
- Reuse the same
Idempotency-Keyfor every retry of one top-up. A new key means a new payment. - A wallet's or facilitator's "success" is not credit. Only a finalized on-chain transfer, verified by Layerbeat, adds credit, and each transfer counts once.
- If a request fails after you paid, replay it or check the payment. If the agent stopped right after paying, the payment watcher still credits a transfer of the exact amount.
The limits of ordinary top-ups apply: $5 minimum and $1,000 maximum per top-up, at most 5 unpaid top-ups at a time and 20 started in 24 hours, and the account must be eligible for funding. Transfers under one cent are ignored. See Credit and billing and Building with agents.