Non-custodial

AMPR API quickstart

Accept crypto payments that settle directly to your wallet. Full machine-readable spec: /openapi.json (import into Postman, Insomnia, or a codegen).

Prefer a typed client? Use the official TypeScript SDK (@ampr/sdk) — AmprClient plus a verifyWebhookSignature() helper, zero dependencies. The curl examples below map 1:1 to its methods.

1 · Authenticate

All /v1/* endpoints take your API key as a Bearer token:

Authorization: Bearer ampr_sk_…

2 · Register a receiving wallet

Funds land in your keys — register an xpub (unique address per invoice) or a payout address per chain.

curl -X POST $BASE/v1/wallets \
  -H "authorization: Bearer $KEY" -H "content-type: application/json" \
  -d '{"chain":"base","kind":"xpub","xpub":"xpub6…"}'

3 · Create an invoice

Send Idempotency-Key so retries never double-charge. Omit rate to use live FX.

curl -X POST $BASE/v1/invoices \
  -H "authorization: Bearer $KEY" -H "content-type: application/json" \
  -H "idempotency-key: order-1024" \
  -d '{
    "order_id": "1024",
    "price": "129.90",
    "currency": "USD",
    "assets": [{"asset":"USDC_BASE"}, {"asset":"USDT_TRON"}]
  }'

Response includes checkout_url — redirect the payer there (wallet + QR, all chains):

{ "id":"inv_…", "status":"pending", "checkout_url":"$BASE/checkout/inv_…", "addresses":[…] }

4 · Receive the payment webhook

Register an endpoint; the response returns a signing secret once. On payment.confirmed, verify the signature, then re-check status before fulfilling.

The X-AMPR-Signature header is t=<unix>,v1=<hex> where v1 = HMAC_SHA256(secret, "<t>.<rawBody>"). Verify over the raw body.

import crypto from "node:crypto";

export function verify(secret, header, rawBody, toleranceSec = 300) {
  const parts = Object.fromEntries(
    header.split(",").map((p) => { const i = p.indexOf("="); return [p.slice(0, i), p.slice(i + 1)]; })
  );
  const t = Number(parts.t);
  if (!Number.isFinite(t) || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false;
  const expected = crypto.createHmac("sha256", secret).update(t + "." + rawBody).digest("hex");
  const a = Buffer.from(expected), b = Buffer.from(parts.v1 || "");
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

5 · Refunds

Non-custodial: send the refund from your wallet, then record the tx hash.

curl -X POST $BASE/v1/invoices/inv_…/refunds -H "authorization: Bearer $KEY" \
  -H "content-type: application/json" -d '{"amount":"25.50"}'
# → then, after broadcasting the on-chain refund:
curl -X POST $BASE/v1/refunds/rfd_…/complete -H "authorization: Bearer $KEY" \
  -H "content-type: application/json" -d '{"tx_hash":"0x…"}'

Invoice statuses

statusmeaning
pendingawaiting payment
confirmingseen on-chain, waiting for confirmations
heldflagged by sanctions screening — manual review
paidfinalized (webhook sent)
underpaid / overpaid / expired / canceledas named