API Payments
Any client that can make HTTP requests and send XRP can pay your API programmatically - no browser or human intervention required. Your API grants access based on the verified payment.
This use case is for developers monetizing an API, a data feed, or any service where payment happens per request. The agent must be built with an XRPL wallet and configured to handle the payment flow — it does not discover payment requirements on its own.
Need agents to pay without any setup on their end?
This flow requires your server to create the payment request and hand the details to the caller. If you want any x402-aware agent to hit your endpoint and pay automatically with no prior arrangement, use x402 instead.
Flow
Agent → POST /payments/requests (creates payment)
Agent → GET /payments/public/{request_id} (gets wallet, tag, amount)
Agent → Sends XRP on-chain via XRPL
Agent → POST /payments/{request_id}/verify (tx_hash)
DropPay → Confirms tx on-chain, marks payment confirmed
Agent → Calls your API with request_id as proof
Your server → Verifies payment was confirmed (webhook or GET /payments/public/{id})
Your server → Returns the API response
Step 1 - Create the payment
The agent calls your endpoint, which creates a DropPay payment and returns the details.
import requests
from flask import Flask, request, jsonify
app = Flask(__name__)
BASE = "https://droppayxrp.com"
API_KEY = "dp_live_your_api_key_here"
PRICE_XRP = 0.5
@app.route("/api/analyze", methods=["GET"])
def create_payment_for_access():
response = requests.post(
f"{BASE}/payments/requests",
headers={"Authorization": f"Bearer {API_KEY}"},
json={
"amount": PRICE_XRP,
"currency": "XRP",
"description": "API access: /api/analyze"
}
)
payment = response.json()
return jsonify({
"payment_required": True,
"request_id": payment["request_id"],
"amount_xrp": payment["amount"],
"merchant_wallet": payment["merchant_wallet"],
"destination_tag": payment["destination_tag"],
"verify_url": f"https://droppayxrp.com{payment['verify_url']}"
}), 402
const BASE = "https://droppayxrp.com";
const API_KEY = "dp_live_your_api_key_here";
const PRICE_XRP = 0.5;
router.get("/api/analyze", async (req, res) => {
const response = await fetch(`${BASE}/payments/requests`, {
method: "POST",
headers: {
"Authorization": `Bearer ${API_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
amount: PRICE_XRP,
currency: "XRP",
description: "API access: /api/analyze"
})
});
const payment = await response.json();
res.status(402).json({
payment_required: true,
request_id: payment.request_id,
amount_xrp: payment.amount,
merchant_wallet: payment.merchant_wallet,
destination_tag: payment.destination_tag,
verify_url: `${BASE}${payment.verify_url}`
});
});
Step 2 - Agent sends XRP and verifies
The agent uses xrpl-py (Python) or xrpl (Node.js) to submit the payment on-chain, then calls verify_url.
import requests
import xrpl
from xrpl.clients import JsonRpcClient
from xrpl.models import Payment
from xrpl.transaction import submit_and_wait
from xrpl.utils import xrp_to_drops
AGENT_WALLET = xrpl.wallet.Wallet.from_seed("agent_seed_here")
NODE = "https://s1.ripple.com:51234/"
def pay_and_call_api(endpoint: str) -> dict:
# 1. Request access -- expect 402
resp = requests.get(endpoint)
if resp.status_code != 402:
return resp.json()
details = resp.json()
# 2. Get exact XRP amount from public endpoint
pub = requests.get(
f"https://droppayxrp.com/payments/public/{details['request_id']}"
).json()
# 3. Send XRP on-chain
# agent.send_xrp is the canonical amount for both XRP and USD-priced payments
client = JsonRpcClient(NODE)
tx = Payment(
account=AGENT_WALLET.address,
amount=xrp_to_drops(float(pub["agent"]["send_xrp"])),
destination=pub["merchant_wallet"],
destination_tag=pub["destination_tag"]
)
result = submit_and_wait(tx, client, AGENT_WALLET)
tx_hash = result.result["hash"]
# 4. Verify payment
verify = requests.post(
details["verify_url"],
json={"tx_hash": tx_hash}
)
assert verify.json()["valid"] is True
# 5. Re-call API with verified payment ID
return requests.get(
endpoint,
headers={"X-Payment-ID": details["request_id"]}
).json()
import { Client, Wallet, xrpToDrops } from "xrpl";
const AGENT_WALLET = Wallet.fromSeed("agent_seed_here");
const NODE = "wss://s1.ripple.com:51233/";
async function payAndCallApi(endpoint) {
// 1. Request access -- expect 402
let resp = await fetch(endpoint);
if (resp.status !== 402) return resp.json();
const details = await resp.json();
// 2. Get exact XRP amount from public endpoint
const pub = await fetch(
`https://droppayxrp.com/payments/public/${details.request_id}`
).then(r => r.json());
// 3. Send XRP on-chain
// agent.send_xrp is the canonical amount for both XRP and USD-priced payments
const client = new Client(NODE);
await client.connect();
const tx = {
TransactionType: "Payment",
Account: AGENT_WALLET.address,
Amount: xrpToDrops(pub.agent.send_xrp.toString()),
Destination: pub.merchant_wallet,
DestinationTag: pub.destination_tag
};
const prepared = await client.autofill(tx);
const signed = AGENT_WALLET.sign(prepared);
const result = await client.submitAndWait(signed.tx_blob);
const txHash = result.result.hash;
await client.disconnect();
// 4. Verify payment
const verify = await fetch(details.verify_url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ tx_hash: txHash })
});
const { valid } = await verify.json();
if (!valid) throw new Error("Payment verification failed");
// 5. Re-call API with verified payment ID
return fetch(endpoint, {
headers: { "X-Payment-ID": details.request_id }
}).then(r => r.json());
}
Step 3 - Grant access on verified payment
When the agent re-calls your endpoint with X-Payment-ID, check the payment status.
import requests
@app.route("/api/analyze")
def analyze():
payment_id = request.headers.get("X-Payment-ID")
if not payment_id:
return create_payment_for_access()
status = requests.get(
f"https://droppayxrp.com/payments/public/{payment_id}"
).json()
if status.get("status") != "paid":
return jsonify({"error": "Payment not confirmed"}), 402
return jsonify(run_analysis())
router.get("/api/analyze", async (req, res) => {
const paymentId = req.headers["x-payment-id"];
if (!paymentId) return createPaymentForAccess(res);
const status = await fetch(
`https://droppayxrp.com/payments/public/${paymentId}`
).then(r => r.json());
if (status.status !== "paid") {
return res.status(402).json({ error: "Payment not confirmed" });
}
res.json(await runAnalysis());
});
Verify response
Notes
- Use
agent.send_xrpfrom the public endpoint as the amount to send. For XRP-priced payments it is the exact XRP amount. For USD-priced payments it is the XRP equivalent at the rate locked when the agent fetches the public endpoint. - Use
destination_tag- it is required for DropPay to match the on-chain payment to the correct request. - Prefer receiving the
payment.confirmedwebhook rather than polling the public endpoint.
Next steps
- droppay-mcp - MCP server that automates this entire flow for agents in Claude Code and Cursor
- Webhooks reference - receive payment notifications server-side
- Agent - on behalf of user - agent pays for a pre-authorized user