StableOps

Quickstart

Get from zero to a stablecoin payment and verified webhook in ten minutes.

1. Install the SDKs

pnpm add @stableops/api-sdk @stableops/wallet-sdk

The API SDK is plain TypeScript with no runtime dependencies beyond Node's crypto. The wallet SDK runs in the browser and asks the user's wallet to send the on-chain transfer.

2. Configure your server client

import { StableOps } from '@stableops/api-sdk'

const client = new StableOps({
  apiKey: process.env.STABLEOPS_API_KEY!,
  environment: 'sandbox', // Sandbox is the default. Switch to "live" only after test.
})

Do not expose STABLEOPS_API_KEY in the browser. Create payment orders and call merchant APIs from your server.

3. Prepare in the Dashboard

Before you create your first order, complete two setup steps in the Dashboard:

  • Import receiving addresses: add the on-chain addresses you accept. StableOps allocates one address from this pool for each payment order.
  • Register a webhook endpoint: enter your callback URL, for example https://your-app.example.com/hooks/stableops.

Webhook secrets are only shown when created or rotated. Store the secret in a server-side environment variable such as STABLEOPS_WEBHOOK_SECRET. For automation, use the Addresses API and Webhook Endpoints API.

4. Create a payment order

const order = await client.paymentOrders.create(
  {
    merchantOrderId: 'sub_89231_2026_06',
    amount: '49.00',
    settlementAsset: 'USDC',
    acceptedAssets: [
      { chain: 'base-sepolia', asset: 'USDC' },
      { chain: 'ethereum-sepolia', asset: 'USDC' },
    ],
    // Auto-expire after 30 minutes; the order moves to `expired` and the address is released.
    expiresAt: new Date(Date.now() + 30 * 60 * 1000).toISOString(),
    metadata: { customerId: 'cus_9821', plan: 'pro_monthly' },
  },
  { idempotencyKey: crypto.randomUUID() },
)

console.log(order.paymentInstructions)
// [
//   { chain: 'base-sepolia', asset: 'USDC', address: '0x...' },
//   { chain: 'ethereum-sepolia', asset: 'USDC', address: '0x...' },
// ]

Return only the order id, amount, and paymentInstructions to your frontend.

5. Pay with the wallet SDK

After the frontend receives the order from your server, use the wallet SDK to ask the browser wallet to send the transfer:

import {
  getInjectedWalletProviders,
  selectWalletPaymentInstruction,
  sendWalletPayment,
} from '@stableops/wallet-sdk'

const { instruction, provider } = selectWalletPaymentInstruction(
  order.paymentInstructions,
  getInjectedWalletProviders(),
)

const sent = await sendWalletPayment({
  provider,
  amount: order.amount,
  instruction,
})

console.log(sent.txHash)

This example picks a payable instruction from the wallets currently available in the browser. For TRON, Solana, or a custom multi-chain selector, see the Wallet SDK. Once the transfer is on-chain, StableOps scanners match the deposit address and dispatch webhooks as the order state changes.

The wallet SDK is a convenience, not a requirement. The payer can settle this transfer any way they like — sending manually from MetaMask / Phantom / TronLink, withdrawing from an exchange, or using another backend script — as long as order.amount lands on the matching chain's paymentInstructions[].address. Scanners always match deposits by (chain, address), regardless of where the funds come from.

6. Verify the signature

Every delivery carries X-Product-Signature: t=…,v1=…. Use the SDK's constant-time helper to validate it before you parse the body.

import { verifySignature } from '@stableops/api-sdk/webhooks'

export async function POST(req: Request) {
  const rawBody = await req.text()
  const result = verifySignature({
    secrets: [process.env.STABLEOPS_WEBHOOK_SECRET!],
    header: req.headers.get('x-product-signature') ?? undefined,
    rawBody,
  })
  if (!result.ok) {
    return new Response(`invalid: ${result.reason}`, { status: 400 })
  }
  const event = JSON.parse(rawBody)
  // Use event.data.payment_order_id and your own ledger to update entitlement.
  return new Response('ok')
}

What you just built

  • Imported receiving addresses and registered a webhook endpoint in the Dashboard.
  • Created an order with a chain-specific deposit address.
  • Sent an on-chain payment through the wallet SDK.
  • Verified a signed webhook delivery end to end.

Next: read Concepts → Confirmations for the state machine, or jump to the API reference.

On this page