Skip to main content
A conversion moves funds from one form to another without requiring the customer to make a new payment. There are two types:
TypeWhat it does
cryptoSwaps a coin on one chain to a different coin or chain (e.g. USDT/Ethereum → SOL/Solana)
fiatConverts crypto balance to fiat and sends it to a saved payout account

How it works

1

Discover supported swap chains and coins

Call GET /conversions/swap-chains and GET /conversions/swap-coins to find what source/destination pairs are available.
2

Check the fee

Call GET /conversions/fee/{chain}/{coin}/{amount} to get a fee quote and estimated output before committing. For crypto swaps, pass toChain and toCoin as query params to get a destination-specific estimate.
3

Create the conversion

Call POST /conversions with the source coin, chain, amount, and destination. The conversion is queued immediately and processed asynchronously.
4

Poll for completion

Call GET /conversions/{id} to track processingStages until the conversion settles.

Prerequisites

Authentication guide

All requests require an HMAC-SHA256 signature. See the authentication guide for code examples.
const BASE = 'https://sandbox-api.tender.cash/v1/api';
// makeHeaders() → { 'x-access-id', 'x-request-id', 'x-timestamp', 'authorization', ... }

Step 1 — Check the fee

Always fetch a fee quote before creating a conversion so you can show the cost to the user.
// Crypto swap fee quote
const feeRes = await fetch(
  `${BASE}/conversions/fee/ethereum/usdt/100?toChain=solana&toCoin=sol`,
  { headers: makeHeaders() }
);
const { data: fee } = await feeRes.json();
/*
{
  amount:          100,
  amountUSD:       100.00,
  fee:             0.5,
  feeUSD:          0.50,
  amountAfterFee:  99.5,
  estimatedOutput: 1.24,
  rate:            0.0125
}
*/

Step 2 — Create a crypto swap

const convRes = await fetch(`${BASE}/conversions`, {
  method: 'POST',
  headers: makeHeaders(),
  body: JSON.stringify({
    type:    'crypto',
    chain:   'ethereum',
    coin:    'usdt',
    amount:  '100',
    toChain: 'solana',
    toCoin:  'sol',
  }),
});

const { data } = await convRes.json();
const conversionId = data.conversion._id;
console.log('Status:', data.conversion.status); // "pending"

Step 2 (alt) — Create a fiat conversion

To convert to fiat, supply a payoutAccountId instead of toChain/toCoin.
const convRes = await fetch(`${BASE}/conversions`, {
  method: 'POST',
  headers: makeHeaders(),
  body: JSON.stringify({
    type:            'fiat',
    chain:           'ethereum',
    coin:            'usdt',
    amount:          '100',
    payoutAccountId: '66aec7de809b7f45c42a49f9',
  }),
});

Step 3 — Poll for completion

async function pollConversion(id, intervalMs = 8000) {
  while (true) {
    const res  = await fetch(`${BASE}/conversions/${id}`, { headers: makeHeaders() });
    const { data } = await res.json();
    const stages = data.processingStages;

    console.log('Stages:', stages);

    const allDone = Object.values(stages).every(s => s.status === 'completed');
    if (allDone) return data;

    const failed = Object.values(stages).find(s => s.status === 'failed');
    if (failed) throw new Error('Conversion failed');

    await new Promise(r => setTimeout(r, intervalMs));
  }
}

Processing stages

StageWhat happens
sendForConversionFunds sent to the conversion provider
conversionStateProvider executes the swap
settleCryptoConverted funds delivered to destination

Error handling

ScenarioAction
Amount below minimumCheck fee.amountAfterFee > 0 before creating
Unsupported swap pairVerify pair exists via /conversions/swap-chains and /conversions/swap-coins
Stage status: "failed"Read the stage’s lastError field; retry or contact support