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
Use xmr_test_… only on your server and pk_test_… in browser integrations. Sandbox checkouts use tchk_… IDs, never accept payment, and can emit signed test webhooks after an explicit status transition.
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. Stored with the checkout and returned by checkout API reads. Current payment webhooks identify the checkout and may include a transaction hash. |
| 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 xmr_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 xmr_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 xmr_live_…. For sandbox embeds, use pk_test_…; keep xmr_test_… on your server./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 paymentPrefer the callback props above. The loader validates both the sender origin and the active iframe before dispatching an event.
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 xmr_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.
