TypeScript API SDK
Install and configure the StableOps TypeScript API SDK to create payment orders, manage addresses and Webhooks, and make typed server-side API calls.
Install
pnpm add @stableops/api-sdkThe default API client targets Node 18+ and edge runtimes that provide global
fetch, AbortController, and crypto.randomUUID. Webhook verification and
the mock server use Node.js-only subpath exports.
Want a runnable end-to-end example?
Configure
import { StableOps } from '@stableops/api-sdk'
const client = new StableOps({
apiKey: process.env.STABLEOPS_API_KEY!,
// Optional. Inject your own fetch (e.g. msw, undici, edge fetch).
fetch: globalThis.fetch,
})Payment orders
const order = await client.paymentOrders.create(
{
merchantOrderId: 'sub_89231_2026_06',
amount: '49.00',
acceptedAssets: [
{ chain: 'base', asset: 'USDC' },
{ chain: 'tron', asset: 'USDT' },
],
// Auto-expire after 30 minutes; the order moves to `expired` and the address is released.
expiresAt: new Date(Date.now() + 30 * 60 * 1000).toISOString(),
},
{ idempotencyKey: crypto.randomUUID() },
)
await client.paymentOrders.retrieve(order.id)
await client.paymentOrders.list({ status: 'detected', limit: 50 })
await client.paymentOrders.cancel(order.id)paymentOrders.create always requires idempotencyKey. Use a UUID derived
from your order id so retries from your worker land on the same record. Amounts
come back as human-readable decimal strings (for example, "49.00"). Keep them
as strings or use a decimal library instead of Number(amount).
Optional amountMode: 'auto' nudges the amount to a unique value (no manual
offset on SHARED addresses). settlementAsset is derived server-side from
acceptedAssets and isn't passed on create.
Checkout sessions (hosted)
checkoutSessions.create returns a hosted (WalletConnect) payment page — redirect
the user to session.url.
const session = await client.checkoutSessions.create(
{
merchantOrderId: 'sub_89231_2026_06',
amount: '49.00',
acceptedAssets: [{ chain: 'base', asset: 'USDC' }],
expiresAt: new Date(Date.now() + 30 * 60 * 1000).toISOString(),
title: 'Pro plan',
successUrl: 'https://your-app.example.com/pay/success',
cancelUrl: 'https://your-app.example.com/pay/cancel',
},
{ idempotencyKey: crypto.randomUUID() },
)
console.log(session.url) // send the user here to payWebhook endpoints
const endpoint = await client.webhooks.createEndpoint({
url: 'https://your-app.example.com/hooks/stableops',
enabledEvents: ['payment.detected', 'payment.confirmed', 'payment.finalized'],
})
// endpoint.secret is only present on create/rotate. Store it somewhere durable.
await client.webhooks.rotateSecret(endpoint.id)Deliveries and replay: client.webhooks.listDeliveries(...), replay(endpointId, eventId),
replayDelivery(deliveryId), replayDeadLetters({ endpointId, limit }).
Errors
Every non-2xx response is thrown as StableOpsError with .status, .code,
.message, and a raw .details body.
import { StableOpsError } from '@stableops/api-sdk'
try {
await client.paymentOrders.create(input, { idempotencyKey: key })
} catch (err) {
if (err instanceof StableOpsError && err.status === 409) {
// Idempotency-key was reused with a different body, etc.
}
throw err
}Local mock server
The SDK ships an in-process mock for tests and demos:
import { StableOps } from '@stableops/api-sdk'
import { MockServer } from '@stableops/api-sdk/mock'
import { verifySignature } from '@stableops/api-sdk/webhooks'
const mock = new MockServer()
const { url } = await mock.listen()
const client = new StableOps({ baseUrl: url })
const order = await client.paymentOrders.create(
{
merchantOrderId: 'mock-order-1',
amount: '49.00',
acceptedAssets: [{ chain: 'base', asset: 'USDC' }],
expiresAt: new Date(Date.now() + 30 * 60 * 1000).toISOString(),
},
{ idempotencyKey: 'mock-order-1' },
)
const endpoint = await client.webhooks.createEndpoint({
url: 'https://example.com/webhooks/stableops',
enabledEvents: ['payment.detected'],
})
const fixture = mock.buildSignedFixture(endpoint.id, 'payment.detected', {
id: order.id,
})
verifySignature({
secret: fixture.secret,
header: fixture.header,
rawBody: fixture.rawBody,
})
await mock.close()The mock implements only the surface area needed for SDK contract tests: payment orders, webhook endpoints, and a signature fixture builder.
How is this guide?
Last updated
Refund integration
Create full or partial StableOps refunds, confirm customer addresses, broadcast from your wallet, register transaction hashes, and reconcile failures safely.
Python API SDK
Install the StableOps Python SDK to create payment orders and checkout sessions, manage Webhook endpoints, verify signatures, and handle API errors and replay.