Skip to main content
Your users can fund their balance on your platform with stablecoins and cash out to their own wallet. Every user gets a permanent deposit address per chain. AssetPay watches those addresses, credits your merchant balance when a deposit clears, and tells you which user it was for. A cashout runs the other way: it is paid out of your merchant balance to an address the user chose.
Crypto for end users is available to verified merchants only. Unverified merchants receive ONCHAIN_MERCHANT_NOT_VERIFIED.

Supported Networks

Only the tokens listed in the address response are credited. On Solana each token has its own account, so ask for the address with the token the user is about to send and wait until tokens lists it. Show your users the token list from the response, never a hard-coded one.
All amounts are in USD cents. USDT and USDC are credited 1:1.

Deposits

1

Ask for the user's address

Call GET /client/crypto/deposit-address?chain=ETH with the client token, or GET /secure/crypto/deposit-address with your API key and a steamId. The first call creates the address. It never changes, so you can cache it and reuse it for every deposit on Ethereum and BNB Smart Chain.
2

Show it to the user

Display address, the network, the accepted tokens and minDepositCents. While status is paused the address is valid but deposits are credited only once the chain resumes.
3

The user sends the tokens

AssetPay sees the transfer within seconds and records a deposit with status pending.
4

The deposit clears

After minConfirmations blocks the deposit becomes completed, your merchant balance is credited, and you receive a crypto_deposit webhook with the steamId. Credit your user when that webhook arrives.

Solana addresses are opened on request

A Solana account costs rent while it is open, so it is opened when the user asks for it and closed again once it has been emptied or has sat unused.
  • Pass token=USDT or token=USDC (default USDC) for the token the user is about to send. USDT and USDC use separate accounts behind the same address, and each one is opened on its own.
  • The first call answers status: "pending" with address: null. Poll the same endpoint with the same token every few seconds until it is active and tokens lists that token. This usually takes under a minute.
  • Call the endpoint again before every later Solana deposit. If the account was closed in the meantime, the call reopens it.
  • ONCHAIN_ADDRESS_CAPACITY (503) means too many of your users have an open Solana account right now. Retry later.

Minimum deposit

The live value is minDepositCents in the address response. A deposit under the minimum is recorded with status below_minimum and is not credited. Tell your users the minimum before they send.

Deposit statuses

Cashouts

A cashout debits your merchant balance. Debit your own user before you let it go through. There are two ways to start one, and they differ in who is trusted:

The approval call

A cashout requested with a client token starts as awaiting_approval. The amount is already held from your merchant balance, and nothing is sent until your server answers. AssetPay sends a signed POST to your callback URL, the same URL and the same signature as the skin withdrawal approval:
Tell it apart from a skin approval by the body: a cashout carries withdrawal, a skin trade carries trade. Inside your handler: verify the signature, check that steamId has at least amountCents on your platform, debit them, then answer 200. If you approve and the payout later ends as failed, rejected or cancelled, give the user their balance back when that webhook arrives.
You need an active callback URL and an API secret. Without them every client cashout is refused with the reason merchant_no_callback_url.

Fees and amounts

amountCents is what leaves your merchant balance. A flat network fee per chain, feeCents, comes out of that amount, and the user receives receiveCents:
The fee depends on the chain and can be zero. Read it from the response rather than hard-coding it. The amount the user receives must be at least $1.00, otherwise the request fails with ONCHAIN_WITHDRAW_BELOW_MIN.

Safe retries

requestId is required. Sending the same requestId for the same user returns the first withdrawal and never creates a second one, so a timed out request can be repeated safely. Use a fresh requestId for every new cashout.

Daily limits

Two rolling 24 hour limits apply: one per end user and one for all of your users together. When one is reached the request fails with ONCHAIN_CLIENT_WITHDRAW_LIMIT (403):
scope is user or merchant, and remainingCents is what can still be withdrawn now. Cashouts that were refused, failed or cancelled do not count.

Cashout statuses

Webhooks

Both flows report to your regular callback URL with the regular signature. The body has one top-level key that tells you what it is: The objects are the same ones the list endpoints return, and the event is the object’s status. awaiting_approval has no webhook of its own: the approval call is that announcement. Webhooks can arrive more than once. Key your handling on id plus status.

Errors