How it works
Get a quote
Call
POST /onramp/quote with the fiat amount and target cryptocurrency. Tender returns a
live exchange rate, an estimated crypto output, and a quoteId that is valid for a short
window (typically 60 seconds).Initiate the on-ramp
Call
POST /onramp/initiate with the quoteId, the customer’s wallet address, and their
contact details. Tender creates a virtual bank account and returns the transfer details to
show your customer.Customer pays
Customer makes a local bank transfer to the virtual account. No crypto wallet or exchange
account is needed on their side — just a standard bank transfer.
Tender processes automatically
Once payment is confirmed, Tender runs the pipeline automatically:
- Forwards the fiat to the swap provider
- Swaps fiat → intermediate crypto (USDT on Tron)
- Delivers the final crypto to the
targetAddresson thetargetChain
Pipeline stages
After payment is confirmed, the on-ramp moves through these stages in order:| Stage | What happens |
|---|---|
payment | Fiat payment confirmed by the payment provider |
payout | Fiat forwarded to the swap provider’s account |
swap | Fiat swapped to intermediate crypto (e.g. USDT on Tron) |
swapWithdraw | Swapped crypto withdrawn to Tender’s swap wallet (skipped by default) |
cryptoSend | Crypto sent from Tender’s swap wallet to the customer’s targetAddress |
status of pending, in_progress, completed, failed, or skipped.
Overall status values
| Status | Meaning |
|---|---|
pending_payment | Waiting for the customer’s bank transfer |
processing | Payment confirmed; pipeline is running |
crypto_sent | Crypto dispatched to targetAddress |
completed | Fully settled |
failed | Terminal failure — see failureReason and stages[*].lastError |
Key concepts
Quotes are required and single-use
Before initiating, you must call/onramp/quote to lock in a rate. The returned quoteId is:
- Time-limited — expires after
time_to_lockseconds (shown asexpiresAt) - Single-use — consumed the moment it is passed to
/onramp/initiate
400 error.
Virtual bank accounts expire
The virtual bank account returned by/onramp/initiate expires after 1 hour. If the customer
does not pay within that window, the on-ramp request moves to failed status.
Supported chains and currencies
Use the discovery endpoints to present your customers with what is available:GET /onramp/chains— chains that support on-ramp deliveryGET /onramp/coins— cryptocurrencies available, optionally filtered by chain
Integration
This section walks through every step of a real on-ramp integration, from discovery to polling for completion. All examples use the sandbox environment.Prerequisites
- A Tender merchant account with API credentials
- Your
TENDER_ACCESS_IDandTENDER_ACCESS_SECRETset as environment variables
Authentication guide
All requests require an HMAC-SHA256 signature. Follow the authentication guide for full details
and code examples in Node.js, Python, and PHP before continuing.
makeHeaders() helper and a BASE constant as described in
that guide:
Step 1 — Discover available chains and coins
Show your customers which blockchains and currencies they can receive.Step 2 — Get a quote
Once the customer has chosen a target currency and entered their fiat amount, fetch a live quote.Step 3 — Show the quote to your customer
Display the estimated output and ask for confirmation before initiating.Step 4 — Initiate the on-ramp
After the customer confirms, call/onramp/initiate with the quoteId and their wallet address.
| Error message | Cause | Fix |
|---|---|---|
"Quote not found or already used" | quoteId was already consumed or never existed | Fetch a fresh quote |
"Quote has expired" | expiresAt passed before /initiate was called | Fetch a fresh quote |
| Joi validation error | quoteId missing, not a UUID, or targetAddress empty | Fix the request body |
Step 5 — Display bank transfer details
Show the returnedbankTransfer details so the customer knows where to send money.
reference — you will use it to poll for status.
Step 6 — Poll for completion
PollGET /onramp/{reference} until status is crypto_sent or completed.
Inspecting stage progress
Thestages object in the status response shows the exact state of each pipeline step.
Error handling
| Scenario | Recommended action |
|---|---|
status: "failed" | Read failureReason and display it; offer the customer a retry |
status: "pending_payment" after 1 hour | The virtual account expired; start a new on-ramp |
Stage status: "failed" | Read stages[*].lastError for the provider-level reason |
| Network error polling | Retry with exponential backoff; the on-ramp reference is idempotent |