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
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.
| Field | Type | Notes |
|---|---|---|
| priceUsd | integer | Price in USD cents. Required unless productId is set. |
| productId | string | Optional. Ties checkout to a subscription product. |
| customerExternalId | string | Optional. Your internal user / customer ID. |
| metadata | object | Optional. Returned verbatim in webhooks. |
| successUrl | string | Redirect after confirmed payment. |
| cancelUrl | string | Redirect if customer cancels. |
| currency | string | XMR by default. Schema accepts BTC, LTC, ETH, USDT_ERC20, USDT_TRC20, SOL, USDC_SPL when enabled on this node. |
{
"priceUsd": 999,
"currency": "XMR",
"customerExternalId": "user_42",
"metadata": { "orderId": "order_8675309" },
"successUrl": "https://yoursite.com/thanks",
"cancelUrl": "https://yoursite.com/cart"
}{
"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.
| Field | Type | Notes |
|---|---|---|
| name | string | Display name for the product. |
| kind | string | ONE_TIME or SUBSCRIPTION. |
| priceUsd | integer | Price in USD cents. |
| interval | string | DAY / WEEK / MONTH / YEAR. Required for SUBSCRIPTION. |
| intervalCount | integer | Number of intervals per period. Defaults to 1. |
| trialDays | integer | Optional free trial before first charge. |
// 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
}
}'ONE_TIME — single payment.SUBSCRIPTION — recurring with grace period.DAY · WEEK · MONTH · YEARwith an optional
intervalCount.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.
pk_live_…) is safe to ship in client-side JS — never your secret sk_live_….window.postMessage./api/v1/products appear automatically.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 readypayment.detectedBuyer's tx visible in mempoolpayment.confirmingOn-chain, building confirmationspayment.confirmedFinalized — safe to fulfillpayment.cancelledQuote expired without paymentListen with window.addEventListener("message", …) if you prefer raw events to the callback props.
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": "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.
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.
