TD Markets

TDME Pay Integration Guide

A step-by-step reference for integrating cryptocurrency deposits and payouts into TD Markets using the TDME Pay service and payment widget.

Part 1

Before You Start

TDME Pay is the payment gateway that runs on top of the TD Markets v4 exchange. It lets you (the merchant) accept crypto payments from your end-users, auto-convert them to a preferred currency, and withdraw funds to external wallets, all through a single REST API and a hosted, customizable payment widget.

Before writing any integration code, it helps to understand the parties involved, the services you will call, and how authentication works. This section covers everything you need in place before creating your first payment.

The three parties

PartyRole
MerchantYour business. Holds an account on the TD Markets exchange, creates payments and payouts, and configures currencies, webhooks and the widget.
End-userYour customer. Interacts only with the TDME Pay widget to choose a currency, get an address and send funds. Requires no authentication.
Sub-merchantAn optional child account created under a parent merchant, with its own fee configuration.

Environments & base URLs

TDME Pay uses your existing TD Markets account. You authenticate once against TD Markets and use the same JWT for every TDME Pay request, so there are no separate credentials to manage. These are the base URLs you will work with:

ServiceBase URLPurpose
TD Markets exchangehttps://tdmarkets.cryptosrvc.comMerchant web platform: sign in, manage API keys, KYC & MFA.
TD Markets GraphQL APIhttps://api.tdmarkets.io/graphqlAuthentication endpoint. Exchange your API key/secret for a JWT.
TDME Pay APIhttps://crypto-pay-service.cryptosrvc.comAll merchant, client and payout REST endpoints.
TDME Pay widgethttps://crypto-pay-widget.cryptosrvc.comHosted checkout widget you redirect end-users to.
Base path

Every REST endpoint in this guide is served under https://crypto-pay-service.cryptosrvc.com and is versioned with a /v1 prefix.

Prerequisites

Make sure you have all of the following before you begin:

  • A merchant account on the TD Markets exchange.
  • An API key and API secret, generated from the exchange platform.
  • KYC verification approved, required to create payments and payouts.
  • MFA enabled, required specifically to create and approve payouts.
  • At least one currency enabled on your account.

Authentication

TDME Pay secures its merchant endpoints with a JWT bearer token. This is the same token you use for TD Markets: obtain it by exchanging your API key and secret through the TD Markets GraphQL API using the service_signin mutation, then send it on every TDME Pay request.

GRAPHQLhttps://api.tdmarkets.io/graphql
GraphQL · mutation
mutation Service_signin {
  service_signin(
    service_api_key: "<your_api_key>",
    service_api_secret: "<your_api_secret>"
  ) {
    jwt
    expires_at
  }
}
FieldDescription
jwtBearer token to send on every authenticated TDME Pay request.
expires_atExpiration as a Unix timestamp in milliseconds. Refresh the token before it elapses.

Send the returned token in the Authorization header of each TDME Pay request:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Keep secrets server-side

Your API key, secret and JWT must never be exposed in client-side code. Perform sign-in and all authenticated calls from your backend.

Public client endpoints

Endpoints under /v1/client/* are public and require no authentication. They are designed to be called by the widget or your checkout front-end on behalf of the end-user.

Additional validations

OperationRequirement
Create paymentKYC approved
Create / approve payoutKYC approved and MFA enabled

Account setup

Once authenticated, configure your merchant account. All of the endpoints below live under /v1/user and require the bearer token.

1 · Check-in

Check-in ensures your TDME Pay user exists and keeps the currency list in sync with the exchange. Run it after initial setup and whenever new currencies become available.

PATCH/v1/user/checkin
curl -X PATCH "https://crypto-pay-service.cryptosrvc.com/v1/user/checkin" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"

2 · Available currencies

Retrieve the currencies available to your account. Use this to learn which currency_id and network pairs you can enable, set as preferred, or accept in payments.

GET/v1/currency
curl -X GET "https://crypto-pay-service.cryptosrvc.com/v1/currency" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"

Response:

{
  "success": true,
  "message": "",
  "currencies": [
    {
      "currency_id": "BTC",
      "type": "Crypto",
      "network": "BITCOIN",
      "min_confirmations": 1,
      "max_confirmations": 3,
      "block_time": 600
    },
    {
      "currency_id": "USDT",
      "type": "Crypto",
      "network": "ETHEREUM",
      "min_confirmations": 1,
      "max_confirmations": 12,
      "block_time": 15
    },
    {
      "currency_id": "USD",
      "type": "Fiat",
      "network": "DEFAULT"
    }
  ]
}
FieldTypeDescription
currency_idstringCurrency symbol, e.g. BTC.
typestringCrypto or Fiat.
networkstringBlockchain network (DEFAULT for fiat).
min_confirmationsintegerMinimum blockchain confirmations. Omitted for fiat.
max_confirmationsintegerConfirmations required to finalize. Omitted for fiat.
block_timenumberApproximate network block time in seconds. Omitted for fiat.
Related lookups

GET /v1/currency/{currencyId} lists the currencies convertible from a given currency, and GET /v1/currency/payout lists the currencies available for payouts.

3 · Preferred currency

The preferred currency is the destination currency credited to your exchange account when auto-conversion happens. It defaults to BTC.

PATCH/v1/user/update-preferred-currency
curl -X PATCH "https://crypto-pay-service.cryptosrvc.com/v1/user/update-preferred-currency" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"preferred_currency_id": "BTC"}'

4 · Currency settings

Enable the currencies you accept and decide, per currency, whether received funds are auto-converted to your preferred currency.

PATCH/v1/user/update-currency-settings
curl -X PATCH "https://crypto-pay-service.cryptosrvc.com/v1/user/update-currency-settings" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "settings": [
      { "currency_id": "BTC",  "network": "BITCOIN",  "enabled": true,  "is_converted": true },
      { "currency_id": "ETH",  "network": "ETHEREUM", "enabled": true,  "is_converted": false },
      { "currency_id": "USDT", "network": "ETHEREUM", "enabled": true,  "is_converted": true },
      { "currency_id": "XRP",  "network": "RIPPLE",   "enabled": false, "is_converted": true }
    ]
  }'
FieldTypeDescription
currency_idstringCurrency symbol, e.g. BTC.
networkstringBlockchain network, e.g. BITCOIN, ETHEREUM.
enabledbooleanWhether end-users may pay in this currency.
is_convertedbooleanAuto-convert received funds to the preferred currency.
How conversion is decided

If a payment currency equals the preferred currency, no conversion is needed. If it is enabled with is_converted: true, funds are converted on arrival. If is_converted is false, funds are kept as-is. A disabled currency cannot be used to pay at all.

5 · Crypto confirmations (optional)

Lower the number of blockchain confirmations required before a payment advances: faster, but riskier. TDME Pay still waits for the network's full confirmation count before finalizing; the reduced threshold only changes the interim status (prefixed Client).

PATCH/v1/user/update-crypto-confirmations
curl -X PATCH "https://crypto-pay-service.cryptosrvc.com/v1/user/update-crypto-confirmations" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "confirmations": [
      { "currency_id": "BTC", "network": "BITCOIN",  "confirmations": 1 },
      { "currency_id": "ETH", "network": "ETHEREUM", "confirmations": 6 }
    ]
  }'

Pricing modes

Every payment is created with one of two pricing modes, which decides when the crypto amount is locked and who carries the price risk.

Mode (API value)Amount lockedRiskBest for
LongLivedQuoteAt quote creationMerchantE-commerce, fixed prices
ConvertOnArrivalAt blockchain confirmationCustomerDonations, open deposits

With LongLivedQuote the end-user must generate a quote that fixes the exact crypto amount for a limited time. With ConvertOnArrival no quote is used; the preferred-currency value is computed from the market rate at the moment the transaction confirms, and you can show an indicative rate via the market-price endpoint.

Payment statuses

A payment moves through the following statuses. The Client variants only appear when you have lowered the confirmation threshold for a currency (see above).

StatusMeaningFinal
NewPayment created; no address generated yet.No
PendingAn address exists and is awaiting funds.No
ProcessingA transaction was detected; awaiting confirmations.No
ClientPartiallyCompletedPartial payment reached the merchant's custom confirmation threshold.No
PartiallyCompletedLess than the required amount was received.No
ClientCompletedFull amount reached the custom confirmation threshold; awaiting full confirmations.No
CompletedFully paid and confirmed.Yes
CompletedProfitConvert-on-arrival completion where the price moved in the merchant's favour.Yes
CompletedLossConvert-on-arrival completion where the price moved against the merchant.Yes
ManualCompletedManually marked as completed by the merchant.Yes
ManualRejectedManually rejected so users can no longer pay this invoice.Yes
ExpiredExpiration elapsed before completion.Yes
Next

With your account configured, continue to the Deposit tab to accept your first payment, or the Payout tab to withdraw funds.

Part 2

Deposit: Accepting Payments

A deposit is the flow where your end-user pays you in crypto.

You create a payment server-side, send the user to the widget, and the widget drives the public client endpoints to quote, generate an address and track confirmations. You are notified of the outcome through webhooks.

🏪
Merchantcreates payment
🧾
Widgetquote + address
👤
End-usersends crypto
🔗
Blockchainconfirmations
🔔
Webhookmerchant notified
1

Create the payment

Call this from your backend when the customer starts checkout.

POST/v1/paymentAuth required
curl -X POST "https://crypto-pay-service.cryptosrvc.com/v1/payment" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "Invoice",
    "pricing_mode": "LongLivedQuote",
    "merchant_order_currency_id": "USD",
    "amount_required": 100.00,
    "merchant_order_id": "ORDER-12345",
    "merchant_order_description": "Premium Subscription",
    "expiration": "01:00:00",
    "fees_in_price": true
  }'
ParameterTypeReq.Description
typestringyesInvoice or OpenDeposit.
pricing_modestringyesLongLivedQuote or ConvertOnArrival.
merchant_order_currency_idstringyes*Invoice currency, e.g. USD. *Required for Invoice.
amount_requireddecimalyes*Amount to collect. Must be > 0. *Required for Invoice.
merchant_order_idstringnoYour order reference (max 36 chars).
merchant_order_descriptionstringnoDescription shown in the widget.
merchant_order_metadataJSONnoCustom JSON echoed back in webhooks.
expirationstringnoFormat HH:MM:SS (or d:HH:MM:SS). Minimum 1 hour.
fees_in_pricebooleannoInclude fees in the quoted amount. Default true.
passthrough_feesbooleannoPass network fees on to the customer.

The response returns the payment with status New. The example below reflects the exact PaymentResponse shape returned by the service:

{
  "success": true,
  "message": "",
  "payment": {
    "id": "857e94e4-f7f0-490d-9a53-9c0c99b47978",
    "owner_user_id": "1a1909b9-f0dc-4de1-a2a8-afaa04e6b482",
    "status": "New",
    "kyt_status": null,
    "currency_id": "USD",
    "expiration": 3599,
    "target_currency_amount_required": 100.5,
    "target_currency_amount_paid": 0,
    "merchant_label": "TD Markets Merchant",
    "merchant_order_id": "ORDER-12345",
    "merchant_order_metadata": null,
    "merchant_order_description": "Premium Subscription",
    "own_merchant_fee": 0.25,
    "type": "Invoice",
    "pricing_mode": "Long Lived Quote",
    "fees_in_price": true,
    "passthrough_fees": false,
    "created": "2026-02-08T12:52:47.006606+00:00",
    "updated": "2026-02-08T12:52:46.986953+00:00",
    "expired": "2026-02-08T13:52:47.006606+00:00"
  }
}
FieldTypeDescription
success / messageboolean / stringEnvelope status and message (empty on success).
idUUIDPayment identifier. Use it to build the widget URL.
owner_user_idstringYour merchant user id.
statusstringPayment status; initially New.
kyt_statusstringKnow-Your-Transaction screening status: Pending, Approved, Partially KYT Rejected or Rejected. null until screening runs.
currency_idstringInvoice currency.
expirationintegerSeconds remaining until expiry.
target_currency_amount_required / _paiddecimalAmount to collect and amount received so far.
merchant_labelstringMerchant display name.
merchant_order_id / _description / _metadatastring / JSONYour order reference, description and custom metadata.
own_merchant_feedecimalCalculated merchant fee.
typestringInvoice or Open Deposit.
pricing_modestringLong Lived Quote or Convert On Arrival.
fees_in_price / passthrough_feesbooleanFee handling flags.
created / updated / expireddatetimeISO-8601 timestamps with offset.
Enum values on the wire

Responses serialize enums using their display label, so pricing_mode comes back as "Long Lived Quote" and type as "Open Deposit" (with spaces). On requests the API accepts either the spaced label or the compact form ("LongLivedQuote", "OpenDeposit"), which is why the request examples above use the compact form.

Payment types

Invoice collects a specific amount and supports quotes. OpenDeposit is open-ended; the customer may send any amount and quotes are not supported (pair it with ConvertOnArrival).

2

Redirect the customer to the widget

Build the widget URL from the payment id and redirect the customer (or embed it in an iframe).

const paymentUrl =
  `https://crypto-pay-widget.cryptosrvc.com/?invoice=${payment.id}`;

// redirect
window.location.href = paymentUrl;

// or embed
const iframe = document.createElement("iframe");
iframe.src = paymentUrl;
document.body.appendChild(iframe);

The remaining steps (3 to 5) are performed by the widget against the public /v1/client endpoints. They are documented here so you can build your own checkout UI if you prefer not to use the hosted widget.

3

Get payment details & create a quote

The widget first loads the payment, then, for LongLivedQuote payments, creates a quote for the currency the customer chose. The quote fixes the exact crypto amount and its expiration.

GET/v1/client/payment/{id}Public

The response returns the payment (with a simplified, client-facing status), the widget configuration and languages, and any transactions received so far:

{
  "success": true,
  "message": "",
  "payment": {
    "id": "857e94e4-f7f0-490d-9a53-9c0c99b47978",
    "client_status": "Pending",
    "currency_id": "USD",
    "currency_network": "DEFAULT",
    "currency_type": "Fiat",
    "target_currency_amount_required": 100.00,
    "total_fee_amount": 0.50,
    "merchant_fee_amount": 0.25,
    "parent_merchant_fee_amount": 0.25,
    "target_currency_amount_paid": 0,
    "target_currency_amount_left": 100.00,
    "expiration": 3540,
    "created": "2026-02-08T12:52:47.006606+00:00",
    "updated": "2026-02-08T12:52:47.006606+00:00",
    "currency_settings": [
      {
        "enabled": true,
        "currency_id": "BTC",
        "network": "BITCOIN",
        "is_converted": true,
        "required_confirmations": 3,
        "is_preferred": true,
        "type": "Crypto",
        "currency_metadata": null
      },
      {
        "enabled": true,
        "currency_id": "USDT",
        "network": "ETHEREUM",
        "is_converted": true,
        "required_confirmations": 12,
        "is_preferred": false,
        "type": "Crypto",
        "currency_metadata": {
          "contract_address": "0xdac17f958d2ee523a2206206994597c13d831ec7",
          "is_native_token": false,
          "is_erc20": true
        }
      }
    ],
    "type": "Invoice",
    "pricing_mode": "Long Lived Quote",
    "is_fees_in_price_quote": true,
    "merchant_order_description": "Premium Subscription",
    "fees_values": [
      { "currency_id": "BTC", "deposit_fee": 0.25, "deposit_flat_fee": 0.0001 }
    ],
    "passthrough_fees": false
  },
  "widget_setting": "{\"primary_color\":\"#E4C030\"}",
  "widget_languages": [
    { "id": "en", "name": "English" }
  ],
  "transactions": [
    {
      "source_currency_id": "BTC",
      "source_currency_network": "BITCOIN",
      "source_amount_received": 0.0025,
      "estimated_target_amount": 100.00,
      "confirmations": 1,
      "crypto_transaction_id": "8dca4a17...089ffb",
      "sub_merchant_fee_amount": null,
      "is_overpayment": false,
      "status": "Detected"
    }
  ]
}

Top-level fields

FieldTypeDescription
success / messageboolean / stringEnvelope status and message.
paymentobjectThe payment details (see below).
widget_settingstringJSON string of the merchant's widget customization.
widget_languagesarrayAvailable widget languages, each { id, name }.
transactionsarrayTransactions received against this payment so far.

payment object

FieldTypeDescription
idUUIDPayment identifier.
client_statusstringSimplified client-facing status: New, Pending, Processing, PartiallyCompleted, Completed, Expired or Rejected.
currency_id / currency_networkstringInvoice currency and its network (DEFAULT for fiat).
currency_typestringCrypto or Fiat. null for open deposits.
target_currency_amount_required / _paid / _leftdecimalAmount to collect, amount paid, and remaining (invoice only).
total_fee_amount / merchant_fee_amount / parent_merchant_fee_amountdecimalFee breakdown. All 0 when passthrough_fees is enabled.
expirationintegerSeconds remaining until expiry.
created / updateddatetimeTimestamps.
currency_settingsarrayCrypto currencies the customer may pay with (see below).
typestringInvoice or Open Deposit.
pricing_modestringLong Lived Quote or Convert On Arrival.
is_fees_in_price_quotebooleanWhether fees are included in the quoted amount.
merchant_order_descriptionstringDescription to show the customer.
fees_valuesarrayPer-currency fees: currency_id, deposit_fee (progressive), deposit_flat_fee (flat). Empty when passthrough_fees is enabled.
passthrough_feesbooleanWhether network fees are passed to the customer.

currency_settings[] and transactions[]

ObjectFields
currency_settings[]enabled, currency_id, network, is_converted, required_confirmations, is_preferred, type, and currency_metadata (contract_address, is_native_token, is_erc20; null for native coins).
transactions[]source_currency_id, source_currency_network, source_amount_received, estimated_target_amount, confirmations, crypto_transaction_id, sub_merchant_fee_amount, is_overpayment, and status (Detected, Completed or Rejected).

Once the customer chooses a currency, create a quote:

POST/v1/client/quotePublic
curl -X POST "https://crypto-pay-service.cryptosrvc.com/v1/client/quote" \
  -H "Content-Type: application/json" \
  -d '{
    "payment_id": "857e94e4-f7f0-490d-9a53-9c0c99b47978",
    "currency_id": "BTC",
    "network": "BITCOIN"
  }'
FieldTypeReq.Description
payment_idUUIDyesThe payment to quote.
currency_idstringyesCurrency the customer will pay in.
networkstringyesBlockchain network (upper-cased server-side).

Response:

{
  "success": true,
  "quote": {
    "quote_id": "7b0d...c31a",
    "source_currency_id": "BTC",
    "source_currency_amount": 0.0025,
    "price": 40000.00,
    "instrument": "BTC-USD",
    "expiration": 120,
    "fees_in_price": true
  }
}
Open deposits

Calling /v1/client/quote for an OpenDeposit payment returns "Quotes are not supported." Skip straight to address generation.

Convert-on-arrival rate

For ConvertOnArrival payments, show an indicative rate instead of a quote: GET /v1/client/market-price/{paymentId}?targetCurrencyId=BTC&sourceCurrencyAmount=100.

4

Generate the payment address

Create the deposit address the customer will send funds to. Pass the quote_id from step 3. For the preferred currency you may omit the quote and an address is generated directly.

POST/v1/client/addressPublic
curl -X POST "https://crypto-pay-service.cryptosrvc.com/v1/client/address" \
  -H "Content-Type: application/json" \
  -d '{
    "payment_id": "857e94e4-f7f0-490d-9a53-9c0c99b47978",
    "quote_id": "7b0d...c31a",
    "currency": "BTC",
    "currency_network": "BITCOIN"
  }'
FieldTypeReq.Description
payment_idUUIDyesThe payment to attach the address to.
quote_idUUIDcond.Quote from step 3. Optional when generating a preferred-currency address without a quote.
currencystringyesCurrency symbol for the address.
currency_networkstringyesBlockchain network (upper-cased server-side).

Response, the payment now moves to Pending:

{
  "success": true,
  "address_id": "a1b2c3d4-...",
  "address": "bc1qhll33g6azrpfwx7pyl6fannjahcrnutk66hk2p",
  "address_currency_id": "BTC",
  "address_currency_network": "BITCOIN",
  "tag_type": null,
  "tag_value": null
}
Destination tags

For tag/memo networks (XRP, XLM, etc.) the response includes tag_type and tag_value, which the customer must include when sending funds.

5

Track the transaction & confirmations

After the customer sends funds, poll the payment's addresses to reflect progress in your UI. The payment advances to Processing when a transaction is detected and to a completed status once the required confirmations are received.

GET/v1/client/{paymentId}/addressesPublic
GET/v1/client/address/{addressId}Public
StageTypical timing
Address generation< 1 second
Transaction detection10 to 60 seconds
Blockchain confirmationsVaries by network
Prefer webhooks over polling

Polling is fine for reflecting live progress in the widget UI, but to avoid hitting request limits we recommend using webhooks as the primary way to track deposit progress, and treating polling only as a lightweight fallback.

6

Receive the result via webhooks

Register a webhook once, and TDME Pay will push payment and transaction events to your server as they happen. Do not rely on webhooks alone; keep polling as a fallback.

POST/v1/webhookAuth required
curl -X POST "https://crypto-pay-service.cryptosrvc.com/v1/webhook" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://yoursite.com/webhooks/payments",
    "label": "Payment Notifications"
  }'

Manage webhooks with GET /v1/webhook, PUT /v1/webhook/{id} (enable/disable) and DELETE /v1/webhook/{id}. You may have up to 5 enabled at once.

Event payloads

Completed transaction event (a confirmed transaction against a payment):

{
  "payment_id": "550e8400-e29b-41d4-a716-446655440000",
  "payment_status": "Pending",
  "transaction_status": "Completed",
  "crypto_transaction_id": "8dca4a17...089ffb",
  "confirmations": 3,
  "address": "bc1qhll33g6azrpfwx7pyl6fannjahcrnutk66hk2p",
  "currency": "BTC",
  "currency_amount_paid": 0.0025,
  "is_converted": true,
  "preferred_currency_amount_received": 100,
  "preferred_currency_id": "USDT",
  "merchant_order_id": "ORDER-12345",
  "merchant_order_metadata": "{ \"custom-property\": \"value\" }"
}

Payment status event (the payment itself changed state, e.g. Completed or PartiallyCompleted):

{
  "payment_id": "550e8400-e29b-41d4-a716-446655440000",
  "payment_status": "Completed",
  "address": "bc1qhll33g6azrpfwx7pyl6fannjahcrnutk66hk2p",
  "confirmations": 3,
  "currency": "USD",
  "currency_amount_paid": 0.0025,
  "merchant_order_id": "ORDER-12345",
  "merchant_order_metadata": "{ \"custom-property\": \"value\" }"
}

Verifying signatures

Every webhook is signed. The signature is the SHA-256 hash of your webhook id concatenated with the raw JSON body, sent in the x-webhook-signature header.

const crypto = require("crypto");

function verify(payload, signature, webhookId) {
  const stringToSign = webhookId + JSON.stringify(payload);
  const expected = crypto.createHash("sha256")
    .update(stringToSign, "utf8").digest("hex");
  return crypto.timingSafeEqual(
    Buffer.from(signature), Buffer.from(expected));
}

Retry policy

If your endpoint does not return a 2xx status, TDME Pay retries with increasing delays. Always return 200 promptly and process asynchronously; make handling idempotent.

AttemptDelay
1Immediate
22 seconds
33 seconds
44 seconds
Part 3

Payout: Withdrawing Funds

A payout withdraws funds from your TDME Pay account to an external wallet.

Payouts use a two-step create-then-approve model: creating a payout leaves it pending, and it only executes once approved with an MFA code.

Requirements

Payouts require both approved KYC and enabled MFA. Without them the create call returns "You should have approved KYC and enabled MFA to create the payout."

📝
CreatePOST /v1/payout
Pendingapproval
🔐
Approve+ MFA code
Processingsending
Completedon-chain

A pending payout can instead be Rejected, which stops it from executing.

1

Create the payout

POST/v1/payoutAuth + KYC + MFA
curl -X POST "https://crypto-pay-service.cryptosrvc.com/v1/payout" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "currency_id": "BTC",
    "network": "BITCOIN",
    "amount": 0.05,
    "destination_address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
    "note": "Refunding"
  }'
ParameterTypeReq.Description
currency_idstringyesCurrency to withdraw.
networkstringyesBlockchain network.
amountdecimalyesAmount to withdraw. Must be > 0.
destination_addressstringyesExternal wallet address.
notestringnoInformational note (max 256 chars).
destination_address_tag_typestringnoTag/memo type (XRP, XLM, ...).
destination_address_tag_valuestringnoTag/memo value (XRP, XLM, ...).
passthrough_feesbooleannoDeduct network fees from the payout amount.

Response, the payout is created with approval_status: Pending and execution_status: New:

{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "currency": "BTC",
  "network": "BITCOIN",
  "amount": 0.05,
  "fee": 0.0001,
  "own_merchant_fee": 0,
  "parent_merchant_fee": 0,
  "final_amount": 0.0499,
  "destination_address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
  "tag_type": null,
  "tag_value": null,
  "blockchain_hash": null,
  "note": "Refunding",
  "approval_status": "Pending",
  "execution_status": "New",
  "payout_passthrough_fees": false,
  "is_refund": false,
  "created_at": "2026-02-18T09:49:09Z",
  "updated_at": "2026-02-18T09:49:09Z"
}
Destination tags

For XRP/XLM-style networks, include destination_address_tag_type and destination_address_tag_value in the request.

2

Approve the payout (with MFA)

Approving moves the payout to approval_status: Approved and execution_status: Processing. It requires a valid MFA code and the source wallet id.

PATCH/v1/payout/{id}/approveAuth + MFA
curl -X PATCH "https://crypto-pay-service.cryptosrvc.com/v1/payout/3fa85f64-.../approve" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "mfa_code": "123456",
    "wallet_id": "wallet_abc123"
  }'
FieldTypeReq.Description
mfa_codestringyes6-digit code from your authenticator.
wallet_idstringyesSource wallet id to draw funds from.
Invalid MFA

An incorrect code returns 400 with "Invalid MFA code". The payout stays pending and can be retried.

Reject a payout

Reject a still-pending payout so it never executes. No MFA is required to reject.

PATCH/v1/payout/{id}/rejectAuth required
curl -X PATCH "https://crypto-pay-service.cryptosrvc.com/v1/payout/3fa85f64-.../reject" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"

The payout returns with approval_status: Rejected and execution_status: Rejected.

Listing payouts & helper endpoints

Retrieve a paginated, filterable list of payouts.

GET/v1/payout/listAuth required
curl -X GET "https://crypto-pay-service.cryptosrvc.com/v1/payout/list?approvalStatus=Pending&pageSize=20" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"
Query paramDescription
currencyFilter by currency.
approvalStatusPending, Approved or Rejected.
executionStatusNew, Processing, Completed or Rejected.
startDate / endDateDate range filter.
searchSearch by note or id.
pageSizeItems per page. Default 10.
pageIndexPage number (1-based). Default 1.

Other payout endpoints

Get a payout by id

Returns the full payout record (same shape as the create and approve responses).

GET/v1/payout/{id}Auth required
curl -X GET "https://crypto-pay-service.cryptosrvc.com/v1/payout/3fa85f64-5717-4562-b3fc-2c963f66afa6" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"
{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "currency": "BTC",
  "network": "BITCOIN",
  "amount": 0.05,
  "fee": 0.0001,
  "parent_merchant_fee": 0,
  "own_merchant_fee": 0,
  "final_amount": 0.0499,
  "destination_address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
  "tag_type": null,
  "tag_value": null,
  "exchange_payment_id": "a7f3c1b2-9d84-4e21-8c0a-1f2e3d4c5b6a",
  "blockchain_hash": "a3f19ec1f6bf2fb8b64a9392e32dac433e05bc5b288468f01b4277b1c82e893b",
  "note": "Refunding",
  "approval_status": "Approved",
  "execution_status": "Completed",
  "created_at": "2026-02-18T09:49:09.379Z",
  "updated_at": "2026-02-18T09:51:12.500Z",
  "kyt": null,
  "payout_passthrough_fees": false,
  "is_refund": false,
  "manual_refund": null
}
FieldTypeDescription
feedecimalExchange (network) fee.
parent_merchant_fee / own_merchant_feedecimalMerchant fee breakdown.
final_amountdecimalAmount delivered after fees.
tag_type / tag_valuestringDestination tag/memo (XRP, XLM, etc.), otherwise null.
exchange_payment_idstringUnderlying exchange transaction reference.
blockchain_hashstringOn-chain transaction hash once broadcast.
approval_status / execution_statusstringSee the status reference above.
kytobjectKnow-Your-Transaction screening result (status, risk score, decision reason, analysed timestamp). null until screening runs.
payout_passthrough_fees / is_refundbooleanFee-passthrough and refund flags.
manual_refundobjectPresent only for manual refunds: transaction_id, payment_id, reason. Otherwise null.

Estimate the payout fee

Preview the fee and net amount before creating a payout. Optional query passthroughFees (true/false) mirrors the create-payout flag.

GET/v1/payout/fee-estimation/{currency}/{network}/{amount}Auth required
curl -X GET "https://crypto-pay-service.cryptosrvc.com/v1/payout/fee-estimation/BTC/BITCOIN/0.05" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"
{
  "fee_estimation": 0.0001,
  "final_amount": 0.0499,
  "fees": {
    "parent_merchant_fee": 0,
    "merchant_fee": 0,
    "exchange_fee": 0.0001
  }
}
FieldTypeDescription
fee_estimationdecimalTotal estimated fee for the payout.
final_amountdecimalNet amount delivered after fees.
feesobjectBreakdown: parent_merchant_fee, merchant_fee, exchange_fee.

Get withdrawal info for a currency

Returns the account balance, payout limits and pending payout total for a currency. Optional query walletId targets a specific source wallet.

GET/v1/payout/info/{currencyId}Auth required
curl -X GET "https://crypto-pay-service.cryptosrvc.com/v1/payout/info/BTC" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"
{
  "account_balance": {
    "free_balance": 1.25,
    "total_balance": 1.30,
    "exposed_balance": 0.05
  },
  "payment_limit": {
    "currency": "BTC",
    "payout_minimal_amount": 0.0005,
    "payout_daily_limit": 5.0,
    "payout_weekly_limit": 20.0,
    "payout_monthly_limit": 50.0
  },
  "pending_payouts_amount": 0.05
}
FieldTypeDescription
account_balanceobjectfree_balance, total_balance, exposed_balance.
payment_limitobjectcurrency, payout_minimal_amount, and remaining payout_daily_limit / payout_weekly_limit / payout_monthly_limit.
pending_payouts_amountdecimalTotal of payouts awaiting execution.

Get payout address history

Previously used destination addresses, so customers can reuse them.

GET/v1/payout/addresses-historyAuth required
curl -X GET "https://crypto-pay-service.cryptosrvc.com/v1/payout/addresses-history" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"
{
  "addresses": [
    {
      "destination_address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
      "network": "BITCOIN",
      "currency": "BTC"
    },
    {
      "destination_address": "rN7n3473SaZBCG4dFL83w7a1RXtXtbk2D9",
      "network": "RIPPLE",
      "currency": "XRP",
      "address_tag_type": "tag",
      "address_tag_value": "324234"
    }
  ]
}

Each entry has destination_address, network and currency. address_tag_type and address_tag_value are only present for tag/memo networks.

Get payout payment routes

The currency and network combinations available for payouts.

GET/v1/payout/payment-routesAuth required
curl -X GET "https://crypto-pay-service.cryptosrvc.com/v1/payout/payment-routes" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"
{
  "payment_routes": [
    { "currency": "BTC", "crypto_network": "BITCOIN" },
    { "currency": "XRP", "crypto_network": "RIPPLE", "crypto_tag_type": "tag" }
  ]
}

Each route has currency and crypto_network. crypto_tag_type is only present for tag/memo networks.

Status reference

approval_statusexecution_statusMeaning
PendingNewCreated, waiting for approval.
ApprovedProcessingApproved with MFA; funds are being sent.
ApprovedCompletedBroadcast on-chain; a blockchain_hash is present.
RejectedRejectedRejected before execution.