Get started

Accept USDT in four steps

Axiomis Pay is a non-custodial USDT checkout. You create an invoice with one API call, the customer pays on our page, the money lands on addresses derived from your public key, and a signed webhook tells you it is paid. We never hold your funds or your keys.

Sign up with an email and a password: test mode, a test key and simulated payments are available right away. Live keys come after a short business form and a review (usually within 24 hours).

How it works

  1. Your server creates an invoice in USD. The response has a hosted_url and an embed_url.
  2. The customer opens the page, picks a coin and network (for example USDT on TRON or USDC on Base) and accepts the terms. Only then do we assign a deposit address from your pool.
  3. The customer sends USDT from any wallet or exchange. The gateway reads the chain itself and waits for enough confirmations.
  4. You get invoice.paid by webhook with the amount received. Grant access. Funds stay on your addresses until you sweep them to your cold wallet.

1. Get a test key

A key looks like axp_test_… (test mode) or axp_live_… (live). It is shown once: store it in your secrets manager, never in the browser or in git. A test key only works with test networks, so you can build the whole integration without real money. See Test mode.

Check the key:

curl
curl https://pay.axiomis.app/api/v1/merchant -H "Authorization: Bearer $AXIOMISPAY_KEY"

# {"ok":true,"merchant":{"id":"…","slug":"acme","name":"Acme"},"mode":"test"}

2. Create an invoice

Install the SDK (Node.js 18+, TypeScript types included) or call the HTTP API directly.

Terminal
npm install @axiomis-pay/sdk
import { AxiomisPayClient } from '@axiomis-pay/sdk';

const pay = new AxiomisPayClient({
  apiKey: process.env.AXIOMISPAY_KEY, // axp_test_... in test mode
  baseUrl: 'https://pay.axiomis.app/api',
});

const invoice = await pay.createInvoice(
  {
    amount_usd_cents: 2900,            // $29.00
    external_order_id: order.id,       // your order ID
    customer_ref: user.id,             // your customer ID
    description: 'Pro plan, 1 month',
    success_url: 'https://yourapp.com/thanks',
  },
  { idempotencyKey: `order-${order.id}` },
);

// Send the customer to the hosted page, or embed invoice.embed_url in an iframe.
return redirect(invoice.hosted_url);

Amounts are integers in US cents, from 100 ($1) to 10000000 ($100,000). 1 USDT is treated as 1 USD. The invoice is open for 60 minutes after the customer picks a network. Full field list: Create an invoice.

3. Show the checkout

Redirect the customer to hosted_url, or embed embed_url in an iframe on your checkout page. The iframe needs your domain on the allow list: tell us the exact origins, for example https://yourapp.com. Details: Checkout page.

HTML
<iframe
  src="https://pay.axiomis.app/embed/i/inv_…?compact=1"
  title="Payment"
  style="width:100%;border:0;min-height:560px"
  allow="clipboard-write"
></iframe>

4. Handle the webhook

Give us your webhook URL (HTTPS) and you get a secret whsec_…. Every event is signed; verify the signature on the raw body, answer 2xx fast, and process each event.id once.

Node.js (Express)
import express from 'express';
import { verifyWebhook, PAID_STATUSES } from '@axiomis-pay/sdk';

const app = express();

// Raw body: the signature covers the exact bytes we sent.
app.post('/webhooks/axiomispay', express.raw({ type: '*/*' }), async (req, res) => {
  let event;
  try {
    event = verifyWebhook(req.body, req.headers['axiomispay-signature'], process.env.AXIOMISPAY_WEBHOOK_SECRET);
  } catch {
    return res.sendStatus(400);
  }
  const invoice = event.data.invoice;
  if (PAID_STATUSES.includes(invoice.status)) {
    // Idempotent: the same event.id may arrive more than once.
    await grantAccessOnce(invoice.external_order_id, event.id);
  }
  res.sendStatus(200);
});

Grant access only from a verified webhook or from GET /v1/invoices/:id on your server. Never trust a redirect to success_url or a postMessage from the iframe: both can be faked by the customer.

Treat paid, paid_overpaid and paid_late as paid (PAID_STATUSES in the SDK). Other statuses and what to do with them: Statuses and edge cases.

Go live

  • We set up your live pool from your xpub (public key) and check the first addresses with you. Your seed phrase never leaves your device.
  • Switch the key to axp_live_…. Your webhook endpoint stays the same; live events have mode: "live". Live success_url and cancel_url must be HTTPS.
  • Run one small real payment end to end before you announce it.
Quickstart