API reference

XMRgate REST API

JSON over HTTPS. Bearer-token auth. All monetary amounts are in USD cents as integers, so $9.99 is sent as 999.

Base URL

https://xmrgate.com

Auth

Authorization: Bearer xmr_live_...

Rate limit

100 req / min per key

Endpoints

GET
/api/v1/currencies
List currencies enabled on this node
POST
/api/v1/checkouts
Create a new checkout
GET
/api/v1/checkouts/:id
Fetch checkout status and metadata
GET
/api/v1/checkouts
List checkouts (filterable)
POST
/api/v1/donations
Create a donation checkout
GET
/api/v1/donations/:id
Fetch donation status
GET
/api/v1/donations
List donations
POST
/api/v1/products
Create a product or subscription plan
GET
/api/v1/products
List all products
PATCH
/api/v1/products/:id
Update product fields
POST
/api/v1/subscriptions
Create a subscription
GET
/api/v1/subscriptions/:id
Fetch a subscription
GET
/api/v1/subscriptions
List subscriptions
DELETE
/api/v1/subscriptions/:id
Cancel a subscription
GET
/api/v1/me
Fetch merchant account details
GET
/api/v1/balances
List merchant balances by currency
PUT
/api/v1/payout-settings
Set payout wallet and threshold
POST
/api/v1/payouts
Request a payout

POST /api/v1/checkouts

Creates a new checkout. Returns a hosted pay-page URL to redirect the customer to. Customer funds are received by XMRgate platform wallets, credited to your merchant balance after confirmation, then paid out to your configured wallet after platform fees and network fees are accounted for.

FieldTypeNotes
priceUsdintegerPrice in USD cents. Required unless productId is set.
productIdstringOptional. Ties checkout to a subscription product.
customerExternalIdstringOptional. Your internal user / customer ID.
metadataobjectOptional. Returned verbatim in webhooks.
successUrlstringRedirect after confirmed payment.
cancelUrlstringRedirect if customer cancels.
currencystringXMR by default. Schema accepts BTC, LTC, ETH, USDT_ERC20, USDT_TRC20, SOL, USDC_SPL when enabled on this node.
POST /api/v1/checkouts
{
  "priceUsd": 999,
  "currency": "XMR",
  "customerExternalId": "user_42",
  "metadata": { "orderId": "order_8675309" },
  "successUrl": "https://yoursite.com/thanks",
  "cancelUrl": "https://yoursite.com/cart"
}
201 Created
{
  "id": "ck_a1b2c3d4",
  "status": "PENDING",
  "currency": "XMR",
  "payToAddress": "84Hsk…q9Lm",
  "amount": {
    "atomic": "12345678900",
    "display": "0.012345678900"
  },
  "priceUsd": 999,
  "usdRate": 162.45,
  "rateLockedAt": "2026-05-17T10:00:00Z",
  "expiresAt": "2026-05-17T10:30:00Z",
  "payPageUrl": "https://xmrgate.com/pay/ck_a1b2c3d4"
}

POST /api/v1/products

Create a subscription product or one-time offer. For one-time products, reference the productId in each checkout. For subscriptions, call GET /api/v1/productsafter creation and use the returned plan.idwith POST /api/v1/subscriptions. XMRgate tracks the subscription lifecycle and webhooks you on renewals, grace periods, and cancellations.

FieldTypeNotes
namestringDisplay name for the product.
kindstringONE_TIME or SUBSCRIPTION.
priceUsdintegerPrice in USD cents.
intervalstringDAY / WEEK / MONTH / YEAR. Required for SUBSCRIPTION.
intervalCountintegerNumber of intervals per period. Defaults to 1.
trialDaysintegerOptional free trial before first charge.
POST /api/v1/products -> 201 Created
// Request
{
  "name": "Pro tier (monthly)",
  "kind": "SUBSCRIPTION",
  "priceUsd": 1999,
  "interval": "MONTH",
  "intervalCount": 1,
  "trialDays": 7
}

// Response
{
  "id": "prod_xyz789",
  "name": "Pro tier (monthly)",
  "kind": "SUBSCRIPTION",
  "priceUsd": 1999,
  "active": true,
  "createdAt": "2026-05-17T10:00:00Z"
}

Products & subscription tiers

Create the catalog once via the API (or your dashboard). Every product — one-time payments and recurring subscription tiers — automatically appears in the embedded checkout widget and in any custom integration that calls GET /api/public/products?publishableKey=…. Prices are in USD cents; the gateway converts to the buyer's chosen crypto at quote time using a locked rate good for 30 minutes.

Create a one-time product

curl -X POST https://xmrgate.com/api/v1/products \
  -H "Authorization: Bearer sk_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Lifetime access",
    "description": "One payment, forever",
    "kind": "ONE_TIME",
    "priceUsd": 9900
  }'

Create a subscription tier

curl -X POST https://xmrgate.com/api/v1/products \
  -H "Authorization: Bearer sk_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Premium monthly",
    "description": "Top tier with recurring billing",
    "kind": "SUBSCRIPTION",
    "priceUsd": 4999,
    "plan": {
      "interval": "MONTH",
      "intervalCount": 1,
      "trialDays": 7,
      "gracePeriodDays": 3
    }
  }'
Tier kinds
ONE_TIME — single payment.
SUBSCRIPTION — recurring with grace period.
Intervals
DAY · WEEK · MONTH · YEAR
with an optional intervalCount.
Auto-display
Every active product shows up in the embed widget immediately. No second deploy on your side.

Drop-in embed widget

Add a single <script> tag with your publishable key. The widget renders your full catalog (one-time, subscriptions, custom amounts) in a hosted modal with a Coinbase-style currency picker and real coin logos. The buyer never leaves your site.

Publishable key (pk_live_…) is safe to ship in client-side JS — never your secret sk_live_….
Each click opens an iframe modal; payment status is posted back via window.postMessage.
Products you create in /api/v1/products appear automatically.
Dynamically lists only currencies actually online — no hardcoding.

1. Drop the script in

<script
  src="https://xmrgate.com/embed.js"
  data-pk="pk_live_…"
  async
></script>

2. Add buttons

<!-- Catalog (every product) -->
<button data-xmrgate-pay>
  Pay with crypto
</button>

<!-- Specific product or subscription -->
<button data-xmrgate-pay
  data-product="fdc9238f-…">
  Subscribe to Premium
</button>

<!-- Fixed amount / donation -->
<button data-xmrgate-pay
  data-amount="2500"
  data-mode="donation"
  data-name="Tip $25">
  Tip $25
</button>

Programmatic API

XMRGate.open({
  productId: "fdc9238f-…",
  customerEmail: "alice@example.com",
  successUrl: "https://you.com/thanks",
  cancelUrl: "https://you.com/cancel",
  onSuccess: (e) => {
    // e.checkoutId, e.txHash, e.status
    fulfill(e.checkoutId);
  },
  onCancel: () => console.log("cancelled"),
  onClose: (reason) => {},
});

Lifecycle events

checkout.createdModal generated a checkout; payUrl ready
payment.detectedBuyer's tx visible in mempool
payment.confirmingOn-chain, building confirmations
payment.confirmedFinalized — safe to fulfill
payment.cancelledQuote expired without payment

Listen with window.addEventListener("message", …) if you prefer raw events to the callback props.

Public REST counterpart. If you'd rather not iframe, the same endpoints power your own UI: GET /api/public/products?publishableKey=…, POST /api/public/checkouts, POST /api/public/subscriptions, GET /api/public/checkouts/:id. All accept the publishable key in the request body or query string — no secret key needed client-side.

Webhooks

Every status change fires a POST to your registered endpoint. Verify the X-XMR-Signature header before fulfilling. We retry with exponential backoff: 1 min, 5 min, 30 min, 2 h, 12 h. Return 2xx immediately and process asynchronously.

Event types
checkout.detected
First on-chain sighting of a payment tx
checkout.confirmed
10 confirmations reached, exact amount
checkout.overpaid
10 confirmations, more than expected
checkout.underpaid
10 confirmations, less than expected
checkout.expired
TTL passed with no payment
subscription.created
First payment for a subscription
subscription.payment_due
Subscription invoice generated and awaiting payment
subscription.activated
Initial subscription payment confirmed
subscription.renewed
Renewal payment confirmed
subscription.cancelled
Subscriber cancelled (runs to period end)
subscription.expired
Period ended, no renewal in grace window
payout.broadcast
Payout sent to your wallet
payout.confirmed
Payout confirmed on-chain
payout.failed
Payout failed — funds restored to balance
POST your-endpoint · X-XMR-Signature: t=…,v1=…
{
  "event": "checkout.confirmed",
  "data": { "checkoutId": "ck_a1b2c3d4" },
  "timestamp": 1747476000
}

Signature algorithm

HMAC-SHA256(secret, t + "." + rawBody)

Extract t and v1 from the header. Re-compute the HMAC over the concatenated string. Use a constant-time comparison to prevent timing attacks.

Verify a webhook

The Node SDK exposes a helper that parses the signature, verifies it, and returns the typed payload. In other stacks, implement the same HMAC check. Pass the raw request body — never the pre-parsed JSON.

View SDK install instructions →
import { XMRGate } from "@xmrgate/sdk";

app.post("/webhook", express.text({ type: "*/*" }), (req, res) => {
  let event;
  try {
    event = XMRGate.webhooks.constructEvent(
      req.body,
      req.headers["x-xmr-signature"],
      process.env.XMRGATE_WEBHOOK_SECRET
    );
  } catch { return res.status(401).end(); }

  res.status(200).end(); // ack before processing

  if (event.event === "checkout.confirmed") {
    grantAccess(event.data.checkoutId);
  }
});

Authentication

Every request requires a bearer token in the Authorization header. Keys are issued at merchant onboarding and never stored in plaintext on our end — only a bcrypt hash. If a key leaks, revoke it from the dashboard immediately.

Authorization: Bearer sk_live_aBcDeFgHiJkLmNoP…

Errors

All errors return a JSON body with a message field. Standard HTTP status codes apply.

400Bad request — missing or invalid fields
401Invalid or missing API key
404Resource not found
429Rate limit exceeded
500Internal error — retry with backoff

Use the official Node SDK

The public SDK wraps only the documented merchant API and webhook verifier. It does not include backend internals, wallet code, database code, or operational secrets. Install it from npm after publication, or use the downloadable package hosted on this site.