Skip to content

Quickstart

Zero to a verified API call in under ten minutes.

Sign in to the merchant dashboard with email-OTP, attach a wallet, and create an API key under Settings → API keys. The raw key is shown once — store it in your secret manager. Server keys are prefixed sk_live_*.

To test without touching mainnet, create sessions with sandboxMode: true (see step 5) — same key, same host, payment options restricted to testnet chains.

Create a payment session:

Terminal window
curl -X POST https://api.harness.stablecoinx.com/v1/sessions \
-H "Authorization: Bearer $STABLECOINX_KEY" \
-H "Content-Type: application/json" \
-d '{
"amount": "10.00",
"merchantName": "Acme Inc.",
"paymentOptionSymbols": ["USDC", "USDT", "USDe"],
"expiresInSec": 1800,
"successUrl": "https://example.com/thanks"
}'

Response:

{
"id": "a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"paymentUrl": "https://pay.stablecoinx.com/session/a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"status": "awaiting_payment",
"expiresAt": "2026-05-26T17:30:00Z",
"createdAt": "2026-05-26T17:00:00Z"
}

paymentUrl is what you send the customer. Per-chain deposit addresses live inside the session’s paymentOptions[] (fetch them via GET /v1/sessions/:id).

Redirect the customer to paymentUrl. They pick a stablecoin + chain and transfer the exact amount to the displayed deposit address. Then either receive a webhook (recommended) or poll status (no auth required for this endpoint):

Terminal window
curl https://api.harness.stablecoinx.com/v1/sessions/a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d/status
# → { "status": "confirming", "webhookDelivered": true }

The session moves through pending → awaiting_payment → confirming → bridging → completed (terminal: completed | failed | expired). When status is completed, funds have settled to your merchant wallet as sUSDe.

Each delivery carries X-StablecoinX-Signature: t=<unix>,v1=<hmac> and X-StablecoinX-Event-Id. The signature is HMAC-SHA256 of "{timestamp}.{rawBody}" using your endpoint secret. Verify on receipt, compare in constant time, and reject signatures older than ~5 minutes:

import crypto from "node:crypto";
// Capture the *raw* body — JSON-parsing first will reshuffle bytes and break the HMAC.
const sigHeader = req.header("X-StablecoinX-Signature") ?? "";
const m = /^t=(\d+),v1=([0-9a-f]+)$/.exec(sigHeader);
if (!m) return res.status(400).end();
const [, tsStr, theirSig] = m;
const ts = Number(tsStr);
if (Math.abs(Date.now() / 1000 - ts) > 300) return res.status(400).end();
const expected = crypto
.createHmac("sha256", process.env.STABLECOINX_WEBHOOK_SECRET!)
.update(`${ts}.${req.rawBody.toString("utf8")}`)
.digest("hex");
const ok = crypto.timingSafeEqual(
Buffer.from(theirSig, "hex"),
Buffer.from(expected, "hex"),
);
if (!ok) return res.status(401).end();

Today the API emits a single event type, session.confirming. More event types are coming.

There is one production host and one API shape; test mode is per-session, not a separate environment:

  • Test: pass sandboxMode: true on POST /v1/sessions. Payment options are restricted to testnet chains (Base Sepolia, Arbitrum Sepolia, Ethereum Sepolia); pay with testnet stablecoins. The rest of the pipeline (forwarder, swap, webhook) is the same code path mainnet runs.
  • Live: pass sandboxMode: false (or omit it). Supported mainnets: base, arbitrum, ethereum.

sandboxMode is strictly validated — send a real boolean; a non-boolean value returns 400.