IntegrateMerchant API guide
Integrate

Merchant API

Use the merchant API to create payments and invoices, read their status and send payouts.

Markdown

Your shop's server calls the merchant API to create payments and invoices and to read their status. The API keeps the paths, fields, statuses and IPN signature of the common hosted-gateway API. The API reference lists every field and schema.

Base URL

The base URL is your site URL (TURMITE_SITE_URL). Every path starts with /v1/.

Site URL Example request
https://pay.example.com https://pay.example.com/v1/payment
https://example.com/pay https://example.com/pay/v1/payment

A project's checkout domain also serves the merchant API. A shop can use its own checkout domain as the base URL. See Projects and checkout domains.

Authentication

Send the project API key in the x-api-key header. Keys start with tm_. Each key belongs to one project, and the API only shows data of that project.

  1. Open the dashboard and select the project.
  2. Open API keys.
  3. Under Create a key, enter a label and a fresh two-factor code. Then click Create key.
  4. Copy the key. The dashboard shows it one time.

You need the project_admin role to create a key. A developer can revoke a key. Turmite stores only a SHA-256 hash of each key.

Keep the key on your server. Do not put it in browser code or in a mobile app.

curl https://pay.example.com/v1/status
curl -H "x-api-key: $TM_API_KEY" https://pay.example.com/v1/merchant/coins

The payout endpoints also need a short-lived token from POST /v1/auth. See Payouts.

Requests and responses

  • Send JSON bodies with Content-Type: application/json. Turmite ignores fields that it does not know.
  • Tickers (pay_currency, currency) are lowercase, for example btc or usdttrc20. See Tickers and networks.
  • Request amounts can be a JSON number or a decimal string, for example 25 or "25.00".
  • POST /v1/payment, POST /v1/invoice, POST /v1/invoice-payment and POST /v1/payout answer 201 with the new object. A retry that returns an existing object also gets 201.
  • Payment amounts in responses are JSON numbers. A whole amount is an integer: 25, not 25.0. An amount below 0.00001 can be in exponent form, for example 1e-6. Invoice amounts, payout amounts and estimated_amount are decimal strings.
  • Timestamps are ISO 8601 in UTC, with milliseconds.
  • Each client IP address can send 600 requests per minute. Above that, the API returns 429.
  • An error has this shape. See Errors.
{"status": false, "statusCode": 400, "code": "INVALID_REQUEST_PARAMS", "message": "Currency `btc` is not enabled for this project. Add a Bitcoin wallet in the dashboard."}

Price currencies and rates

price_currency is a currency code that the rate source knows, for example usd, eur or btc. Responses write it in lowercase.

Turmite gets rates from CoinGecko. When CoinGecko fails, it uses Coinbase. It keeps each rate for 60 seconds. USDT and USDC count as 1 USD. When price_currency is the ticker or the symbol of pay_currency, for example usdt for usdttrc20, Turmite does not convert the price.

When no source gives a rate, the request gets 503 with the code UNAVAILABLE. Try again after a minute.

Endpoints

Method Path Auth Use
GET /v1/status none Health of the API
GET /v1/currencies key optional Enabled tickers
GET /v1/full-currencies key optional Details per ticker
GET /v1/merchant/coins key Tickers enabled for the project
GET /v1/min-amount none Smallest payment for a ticker
GET /v1/estimate none Price conversion
POST /v1/payment key Create a payment
GET /v1/payment/{payment_id} key Read one payment
GET /v1/payment key List payments
POST /v1/invoice key Create an invoice with a checkout link
POST /v1/invoice-payment key Create a payment for an invoice
GET /v1/balance key Credited and pending amounts per ticker
POST /v1/auth none Get a token for payouts
POST /v1/payout key and token Create a payout batch
POST /v1/payout/{batch_id}/verify key and token Confirm a batch with a two-factor code
GET /v1/payout/{batch_id} key Read one batch
GET /v1/payout key and token List payouts
POST /v1/payout/validate-address key Check a destination address
GET /v1/payout/fee key Estimate the network fee

"Key optional" endpoints answer for the key's project when you send a key, and for the whole install when you do not. A wrong key gets 401.

Status and currencies

GET /v1/status returns {"message":"OK"} when the API is up. For load balancer checks, use /healthz. It returns ok.

GET /v1/currencies returns the tickers that the key's project accepts. A project accepts a ticker when the ticker and its network are enabled and the project has a wallet on that network. Without a key, the list has the tickers that at least one project accepts.

curl -H "x-api-key: $TM_API_KEY" https://pay.example.com/v1/currencies
{"currencies": ["btc", "xmr", "eth", "usdc", "usdterc20", "usdcbase", "usdttrc20", "sol"]}

GET /v1/full-currencies returns every enabled ticker of the install, with details.

{
  "currencies": [
    {
      "id": 7,
      "code": "USDTTRC20",
      "name": "Tether USD",
      "enable": true,
      "wallet_regex": null,
      "priority": 7,
      "extra_id_exists": false,
      "extra_id_regex": null,
      "logo_url": null,
      "track": false,
      "cg_id": "tether",
      "is_maxlimit": false,
      "network": "tron",
      "smart_contract": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
      "network_precision": "6",
      "ticker": "usdttrc20"
    }
  ]
}

ticker is the value to send as pay_currency. code is the ticker in upper case. enable is true when the project accepts the ticker. network_precision is the number of decimals, as a string. smart_contract is null for a native coin. wallet_regex, extra_id_regex and logo_url are always null.

GET /v1/merchant/coins returns the tickers that the project accepts.

{"selectedCurrencies": ["btc", "usdcbase", "usdttrc20"]}

Estimates

GET /v1/min-amount returns the smallest payment for a ticker.

Parameter Required Description
currency_from yes The ticker, for example btc
currency_to no Copied to the response. The default is currency_from
fiat_equivalent no A fiat code. The response then has fiat_equivalent, the minimum in that currency, when a rate is available
curl "https://pay.example.com/v1/min-amount?currency_from=btc&fiat_equivalent=usd"
{"currency_from": "btc", "currency_to": "btc", "min_amount": 1e-6, "fiat_equivalent": 0.07}

When the operator set no minimum for the ticker, min_amount is a default that depends on the decimals of the ticker. POST /v1/payment refuses an amount below min_amount with 400 and the code INVALID_REQUEST_PARAMS.

GET /v1/estimate converts a price into a ticker amount at the current rate. Turmite rounds the amount up to the decimals of the ticker.

Parameter Required Description
amount yes The price
currency_from yes The price currency, for example usd
currency_to yes The ticker, for example btc
curl "https://pay.example.com/v1/estimate?amount=25&currency_from=usd&currency_to=btc"
{"currency_from": "usd", "amount_from": 25, "currency_to": "btc", "estimated_amount": "0.00041235"}

Payments or invoices

Turmite has two ways to take a payment.

Use Create The customer sees
Your shop shows the address and the amount on its own page POST /v1/payment Your page
Turmite shows the checkout POST /v1/invoice The invoice page at invoice_url, then the payment page

The checkout pages work with JavaScript off. See Checkout.

Create a payment

POST /v1/payment creates a payment with its own deposit address.

Field Required Description
price_amount yes Price in price_currency. It must be more than 0
price_currency yes Currency of the price, for example usd
pay_currency yes Ticker the customer pays with, for example btc. The project must accept it
pay_amount no A fixed amount in pay_currency. Without it, Turmite converts price_amount at the current rate
order_id no Your order number, up to 255 characters. Webhooks include it
order_description no Text for your records, up to 1000 characters
ipn_callback_url no URL that receives a webhook at each status change
success_url no Page for the "Return to store" button after payment
cancel_url no Page for the return button after the payment expires
purchase_id no Your purchase reference, up to 255 characters. Without it, Turmite makes a 10-digit one
idempotency_key no Up to 255 characters. A retry with the same key returns the first payment. You can send it in the Idempotency-Key header instead

The three URLs must be https URLs without a user name or password. A testnet install also accepts http. If the project has Allowed return hosts under Settings → Checkout, success_url and cancel_url must use one of these hosts.

Send idempotency_key when your shop can retry the request, for example after a timeout. Use one key per order. A request with a known key returns the first payment, even when other fields are different.

curl -X POST https://pay.example.com/v1/payment \
  -H "x-api-key: $TM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "price_amount": 25,
    "price_currency": "usd",
    "pay_currency": "btc",
    "order_id": "ORDER-1001",
    "order_description": "Order 1001",
    "ipn_callback_url": "https://shop.example.com/turmite/ipn",
    "idempotency_key": "ORDER-1001"
  }'

The response is 201 with a payment object in the status waiting. Show pay_address and pay_amount to the customer on your own page, or send the customer to payment_url, the hosted payment page. Save payment_id with the order.

{
  "payment_id": "5524759814",
  "invoice_id": null,
  "payment_status": "waiting",
  "pay_address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
  "payin_extra_id": null,
  "price_amount": 25,
  "price_currency": "usd",
  "pay_amount": 0.00041235,
  "actually_paid": 0,
  "pay_currency": "btc",
  "order_id": "ORDER-1001",
  "order_description": "Order 1001",
  "purchase_id": "4709931082",
  "outcome_amount": null,
  "outcome_currency": "btc",
  "network": "btc",
  "network_precision": 8,
  "smart_contract": null,
  "payin_hash": null,
  "payout_hash": null,
  "payment_url": "https://pay.example.com/pay/Q7mX2pK9vT4nB8cR1sW6yZ3h",
  "created_at": "2026-10-23T12:00:00.000Z",
  "updated_at": "2026-10-23T12:00:00.000Z",
  "expiration_estimate_date": "2026-10-23T13:00:00.000Z",
  "parent_payment_id": null,
  "type": "crypto2crypto",
  "time_limit": null,
  "burning_percent": null,
  "payment_extra_ids": null,
  "amount_received": 0.00041235,
  "actually_paid_at_fiat": 0,
  "fee": {"currency": "btc", "depositFee": 0, "withdrawalFee": 0, "serviceFee": 0}
}

In this response, payment_id and purchase_id are strings. See The payment object for the types in the other responses.

A payment expires after the project's payment window, 60 minutes by default. Change it in Settings under Payment window (minutes), from 5 to 10080.

When the network has no free deposit address, or no node is connected for it, the request gets 503 with Retry-After: 30. Send it again after 30 seconds.

Create an invoice

POST /v1/invoice creates an invoice and returns invoice_url. Send the customer to that URL. The customer selects a coin, and Turmite creates the payment.

Field Required Description
price_amount yes Price in price_currency
price_currency yes Currency of the price
pay_currency no Ticker to use. Set it to skip the coin selection on the invoice page
order_id no Your order number
order_description no Text for your records
ipn_callback_url no URL that receives the webhooks of the invoice's payments
success_url no Page for the "Return to store" button
cancel_url no Page for the return button after the payment expires

The limits and URL rules are the same as for POST /v1/payment. The payment that the customer starts copies order_id, order_description, ipn_callback_url, success_url and cancel_url from the invoice.

curl -X POST https://pay.example.com/v1/invoice \
  -H "x-api-key: $TM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "price_amount": 25,
    "price_currency": "usd",
    "order_id": "ORDER-1001",
    "order_description": "Order 1001",
    "ipn_callback_url": "https://shop.example.com/turmite/ipn",
    "success_url": "https://shop.example.com/orders/1001",
    "cancel_url": "https://shop.example.com/cart"
  }'
{
  "id": "4522625843",
  "token_id": "k3J9xQ2vT8mZ5wR1nB7cY4pL",
  "order_id": "ORDER-1001",
  "order_description": "Order 1001",
  "price_amount": "25",
  "price_currency": "usd",
  "pay_currency": null,
  "ipn_callback_url": "https://shop.example.com/turmite/ipn",
  "invoice_url": "https://pay.example.com/i/k3J9xQ2vT8mZ5wR1nB7cY4pL",
  "success_url": "https://shop.example.com/orders/1001",
  "cancel_url": "https://shop.example.com/cart",
  "created_at": "2026-10-23T12:00:00.000Z",
  "updated_at": "2026-10-23T12:00:00.000Z",
  "expiration_estimate_date": "2026-10-24T12:00:00.000Z"
}

id is numeric text. price_amount is a decimal string. An invoice does not change, so updated_at is always created_at. An invoice stays open for the project's invoice window, 1440 minutes (24 hours) by default. Change it in Settings under Invoice window (minutes), from 5 to 43200.

If the project has a checkout domain, invoice_url uses it.

Create a payment for an invoice

POST /v1/invoice-payment creates the payment for an invoice from your own coin picker. Send the invoice id as iid and the ticker as pay_currency. purchase_id is optional. The response is a payment object with invoice_id set. The payment gets the price, the order and the URLs of the invoice.

If the invoice already has an open payment in that ticker, the response is that payment. An expired invoice gets 400.

curl -X POST https://pay.example.com/v1/invoice-payment \
  -H "x-api-key: $TM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"iid": "4522625843", "pay_currency": "usdttrc20"}'

Read payments

GET /v1/payment/{payment_id} returns one payment. A payment of another project gets 404.

curl -H "x-api-key: $TM_API_KEY" https://pay.example.com/v1/payment/5524759814

GET /v1/payment returns a page of payments.

Parameter Description
limit Payments per page, from 1 to 500. The default is 10
page Page number. The first page is 0
sortBy created_at (default), updated_at, payment_id, payment_status, pay_address, price_amount, price_currency, pay_amount, actually_paid, pay_currency, outcome_amount, outcome_currency, order_id, order_description or purchase_id
orderBy asc or desc. The default is desc
dateFrom, dateTo Range of created_at. A date (2026-10-01) or a date-time (2026-10-31T23:59:59Z). A date in dateTo includes the full day
payment_status Only payments with this status
order_id Only payments with this order ID
invoiceId Only payments of this invoice
curl -H "x-api-key: $TM_API_KEY" \
  "https://pay.example.com/v1/payment?limit=10&page=0&sortBy=created_at&orderBy=desc"
{"data": [{"payment_id": 5524759814, "payment_status": "finished"}], "limit": 10, "page": 0, "pagesCount": 1, "total": 1}

The example shows two fields of each payment. Each item in data is a full payment object.

Use webhooks to learn about status changes. Use these endpoints to check a status again or to reconcile.

Balance

GET /v1/balance returns two amounts for each ticker that has payments in the project.

Field Value
amount The sum of outcome_amount of the project's payments, refunded payments excluded
pendingAmount The amount still due on open payments (waiting, confirming, partially_paid, confirmed): pay_amount minus the credited amount
{"btc": {"amount": 0.0153, "pendingAmount": 0.0012}, "usdttrc20": {"amount": 1250.5, "pendingAmount": 0}}

The balance is a total of payments. It is not the balance of a wallet. Payouts do not decrease it. A payment that you mark as refunded leaves it.

Payouts

Payouts send funds from the project's hot wallet. Read Payouts for the setup, the limits and the approval rules.

  1. POST /v1/auth with the email address and password of a dashboard user returns a token. The token is valid for 5 minutes.
  2. POST /v1/payout with the API key and the token creates a batch. The user must be a project admin. Each address must be in the project's address book. New payouts have the status pending-otp-verification.
  3. POST /v1/payout/{batch_id}/verify with the user's current two-factor code releases the batch. The project limits then set each payout to pending or pending-approval.

The full example is in Create a payout with the API. POST /v1/auth accepts 10 attempts per minute for each IP address and for each email address.

The payment object

Field Description
payment_id ID of the payment
invoice_id ID of the invoice as a number, or null
payment_status Status. See Payment statuses
pay_address Deposit address for this payment
payin_extra_id Always null. No supported network needs a deposit memo
price_amount Price in price_currency
price_currency Currency of the price
pay_amount Amount the customer must send, in pay_currency
actually_paid Amount of all deposits seen so far, confirmed or not
pay_currency Ticker the customer pays with
order_id Your order number
order_description Your description
purchase_id Your purchase reference, or a 10-digit number that Turmite makes
outcome_amount Confirmed amount credited to the project, or null before the first credit. Sweep fees are not subtracted
outcome_currency Same as pay_currency
network Network code: btc, xmr, eth, base, polygon, bsc, tron, sol or ton
network_precision Decimals of pay_currency
smart_contract Token contract, or null for a native coin
payin_hash Hash of the latest deposit transaction, or null
payout_hash Hash of the latest sweep from the deposit address to your wallet, or null. Always null on Bitcoin and Monero
payment_url Hosted payment page, <site URL or checkout domain>/pay/<token>
created_at Time of creation
updated_at Time of the last change
expiration_estimate_date End of the payment window
amount_received Amount the project gets in outcome_currency: outcome_amount after the first credit, pay_amount before it
actually_paid_at_fiat actually_paid in price_currency at the rate of the payment. For a fixed pay_amount, the rate is price_amount / pay_amount
fee {currency, depositFee, withdrawalFee, serviceFee}. Turmite takes no fee: depositFee and serviceFee are 0. withdrawalFee is the network fee of the sweeps to your wallet
type Always crypto2crypto
parent_payment_id, time_limit, burning_percent, payment_extra_ids Always null

The types of two IDs depend on the response. Typed clients of the common hosted-gateway API expect these types.

Response payment_id purchase_id
POST /v1/payment, POST /v1/invoice-payment String String
GET /v1/payment/{payment_id}, GET /v1/payment Number Number when it is a plain integer up to 2^53 - 1, else string
Webhooks Number String

The payment object keeps the field names of the common hosted-gateway API. network_precision, smart_contract, payin_hash, payout_hash and payment_url are extra fields.

The checkout page uses a random token in its URL. The numeric payment_id never appears in a checkout link.

Statuses in short

Ship the order when the status is finished. The full list and the order of statuses are in Payment statuses. Webhooks tell you about each change. See Webhooks.