Skip to content

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.

api_server.py
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
api_server.js
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.

agent.py
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()
agent.js
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.

api_grant.py
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

{
  "valid": true,
  "request_id": "dp_xxxxxxxxxx",
  "order_id": "order_1234",
  "tx_hash": "ABC123...",
  "paid_at": "2026-07-15T12:00:00.000000",
  "receipt_url": "/receipt/dp_xxxxxxxxxx"
}
{
  "valid": false,
  "reason": "Transaction not found on ledger"
}

Notes

  • Use agent.send_xrp from 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.confirmed webhook rather than polling the public endpoint.

Next steps