Skip to main content
Onramp lets your customers convert fiat currency into cryptocurrency. This guide covers the full implementation from customer creation to crypto settlement.

What is Onramp?

Onramp lets your customers convert fiat currency (AUD, EUR, GBP, SGD ,USD and Other currencies) into cryptocurrency (USDT, USDC ). The flow works like this:
  1. Customer sends fiat to a virtual account
  2. Coinut detects the deposit
  3. Coinut executes the trade
  4. Crypto arrives at the customer’s wallet or your designated wallet address

Architecture

Partner App -> Coinut API -> Sumsub (KYC) -> Virtual Account -> Deposit Detection -> Trade -> Crypto Transfer

Step-by-Step Implementation

1

Create Customer

Call POST /customer/create with name, email, type="DEPOSITOR", and isIndividual=true.
{
  "name": "John Doe",
  "email": "john@example.com",
  "type": "DEPOSITOR",
  "isIndividual": true,
  "externalId": "user_12345"
}
Store the returned customerId. The customer receives a KYC link via email to complete identity verification.
2

Wait for KYC Approval

Listen for the CUSTOMER_APPROVED webhook event. You can also poll GET /customer/detail to check status.Customer status transitions: CREATEDPENDINGAPPROVED | REJECTED
3

Create Virtual Account

Call POST /virtual-account/create with customerId and currency (AUD, EUR, GBP).For AUD accounts, optionally include a whitelist to pre-register allowed senders.Store all returned VA banking details (PayID, BSB, account number, IBAN, etc.).
4

Add Crypto Address

Call POST /crypto-address/add with:
  • customerId
  • currency="USDT"
  • address — the customer’s wallet address
  • label — a display label
  • networkETH for ERC-20 or TRX for TRC-20
Store the returned crypto address ID. You’ll need it for the autoramp flow.
5

Create Autoramp Flow

Call POST /autoramp-flow/onramp with the fiat source and crypto destination:
{
  "from": {
    "customerId": "cust_abc123",
    "currency": "AUD"
  },
  "to": "crypto_address_id_from_step_4"
}
This links the virtual account deposits to automatic crypto conversion.
6

Display VA to Customer

Show the virtual account details in your UI:
  • AUD: Display PayID (e.g., john.doe@example.com) and BSB + Account Number
  • EUR: Display IBAN and SWIFT/BIC
  • GBP: Display IBAN and Sort Code
  • USD: Display IBAN and Sort Code
  • SGD: Display IBAN and Sort Code
  • other currencies : Contact Coinut support for the exact banking fields
Generate a QR code for PayID to simplify mobile banking transfers.
7

Monitor Deposits

Listen for webhooks:
  • DEPOSIT_RECEIVED — funds detected
  • DEPOSIT_APPROVED — funds cleared
Or poll GET /deposit/list with the customer ID.
8

Track Trade

After deposit approval, listen for:
  • TRADE_CREATED — trade initiated
  • TRADE_SETTLED — trade complete, crypto sent
Or poll GET /trade/list.
9

Confirm Settlement

The TRADE_SETTLED webhook includes txHash. Use it to verify the crypto transaction on-chain:
10

Confirm Settlement

The TRADE_SETTLED webhook includes txHash. Use it to verify the crypto transaction on-chain:

Complete Code Example

const axios = require('axios');
const crypto = require('crypto');

const BASE_URL = 'https://ramp.hexarails.ai';
const API_KEY = process.env.COINUT_API_KEY;
const API_SECRET = process.env.COINUT_API_SECRET;

// ── Authentication Helper ─────────────────────────────
function createSignature(method, path, body = {}) {
  const timestamp = Math.floor(Date.now() / 1000).toString();
  const nonce = crypto.randomUUID();
  const host = new URL(BASE_URL).host;
  const requestBody = method.toUpperCase() === 'GET' ? '' : JSON.stringify(body);
  const bodyHash = requestBody
    ? crypto.createHash('sha256').update(requestBody).digest('hex')
    : '';
  const canonical = [
    method.toUpperCase(),
    host,
    path,
    '',
    bodyHash,
    timestamp,
    nonce,
  ].join('\n');
  const signature = crypto.createHmac('sha256', API_SECRET).update(canonical).digest('hex');
  return { timestamp, nonce, signature };
}

async function coinutRequest(method, path, body = {}) {
  const { timestamp, nonce, signature } = createSignature(method, path, body);

  return axios({
    method,
    url: `${BASE_URL}${path}`,
    headers: {
      'Content-Type': 'application/json',
      'X-API-Key': API_KEY,
      'X-Timestamp': timestamp,
      'X-Nonce': nonce,
      'X-Signature': signature,
    },
    data: body,
  });
}

// ── Step 1: Create Customer ──────────────────────────
async function createCustomer(name, email, externalId) {
  const { data } = await coinutRequest('POST', '/customer/create', {
    name,
    email,
    type: 'DEPOSITOR',
    isIndividual: true,
    externalId, // Links to your internal user system
  });
  return data.customerId;
}

// ── Step 3: Create Virtual Account ───────────────────
async function createVirtualAccount(customerId, currency) {
  const { data } = await coinutRequest('POST', '/virtual-account/create', {
    customerId,
    currency, // 'AUD', 'EUR', 'SGD','USD' or 'GBP',etc
  });
  return data;
}

// ── Step 4: Add Crypto Address ───────────────────────
async function addCryptoAddress(customerId, address, network) {
  const { data } = await coinutRequest('POST', '/crypto-address/add', {
    customerId,
    currency: 'USDT', // 'USDT','USDC'
    address,
    label: 'Customer Wallet',
    network, // 'ETH' .'TRX' or 'MATIC'
  });
  return data.cryptoAddressId;
}

// ── Step 5: Create Onramp Flow ───────────────────────
async function createOnrampFlow(customerId, fiatCurrency, cryptoAddressId) {
  const { data } = await coinutRequest('POST', '/autoramp-flow/onramp', {
    from: {
      customerId,
      currency: fiatCurrency,
    },
    to: cryptoAddressId,
  });
  return data;
}

// ── Full Onramp Orchestration ────────────────────────
async function executeOnramp(user) {
  try {
    // 1. Create customer
    const customerId = await createCustomer(user.name, user.email, user.id);
    console.log('Customer created:', customerId);

    // 2. Wait for KYC approval (webhook or polling)
    // See webhook handler below

    // 3. Create virtual account
    const va = await createVirtualAccount(customerId, 'AUD');
    console.log('VA created:', va.payId);

    // 4. Add crypto address
    const cryptoAddressId = await addCryptoAddress(
      customerId,
      user.walletAddress,
      'TRX' // TRC-20
    );

    // 5. Create autoramp flow
    const flow = await createOnrampFlow(customerId, 'AUD', cryptoAddressId);
    console.log('Onramp flow created:', flow.flowId);

    return {
      customerId,
      payId: va.payId,
      bsb: va.bsb,
      accountNumber: va.accountNumber,
      flowId: flow.flowId,
    };
  } catch (error) {
    console.error('Onramp failed:', error.response?.data || error.message);
    throw error;
  }
}

// ── Webhook Handler ──────────────────────────────────
const express = require('express');
const app = express();
app.use(express.json());

app.post('/webhooks/coinut', (req, res) => {
  const { event, data } = req.body;

  switch (event) {
    case 'CUSTOMER_APPROVED':
      console.log('Customer KYC approved:', data.customerId);
      // Proceed to create VA and flow
      break;

    case 'DEPOSIT_RECEIVED':
      console.log('Deposit received:', data.amount, data.currency);
      break;

    case 'DEPOSIT_APPROVED':
      console.log('Deposit approved, trade will execute');
      break;

    case 'TRADE_SETTLED':
      console.log('Trade settled! TX hash:', data.txHash);
      // Notify customer
      break;

    case 'CUSTOMER_REJECTED':
      console.log('Customer KYC rejected:', data.customerId, data.reason);
      break;
  }

  res.status(200).send('OK');
});

app.listen(3000, () => console.log('Webhook server listening on port 3000'));
Use the externalId field when creating customers to link Coinut records with your internal user system. This makes reconciliation and webhook handling much simpler.

Environment

EnvironmentBase URL
Productionhttps://ramp.hexarails.ai
Sandboxhttps://ramp-sandbox.hexarails.ai
Use small amounts for testing (e.g., 10-50 AUD equivalent). Set up webhook testing with a local tunnel:
# Expose local webhook handler to the internet
npx ngrok http 3000

# Use the HTTPS URL as your webhook endpoint in the Partner Dashboard
# Example: https://abc123.ngrok.io/webhooks/coinut

Go-Live Checklist

  • KYC flow tested end-to-end
  • All webhook events handled (CUSTOMER_APPROVED, DEPOSIT_RECEIVED, DEPOSIT_APPROVED, TRADE_SETTLED)
  • Error handling implemented for API failures and timeouts
  • Retry logic with exponential backoff for idempotent requests
  • Monitoring dashboards set up for deposit and trade volumes
  • IP whitelist configured in Partner Dashboard
  • Webhook signature verification implemented
  • Sandbox testing completed with multiple currencies