cryptoinfraDocs

#Quickstart

Six calls, in order. Each one is copy-paste. Do them once against the sandbox, then switch the key and the base URL to live.

Open as MarkdownFor AI assistants

Before you start: sign up at the dashboard, verify your email, and copy your API key from Settings → API keys. The key is shown once. Keep it on your server — never in a browser or an app.

bash
export CRYPTOINFRA_KEY=ck_live_your_key
export API=https://api.cryptoinfra.build

#1. Tell us where to send events

bash
curl -sX POST $API/v1/developer/webhooks \
  -H "Authorization: Bearer $CRYPTOINFRA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://yourapp.example/cryptoinfra/webhook"}'

The response has endpoint.secret. Store it now — it is never shown again. Every event we send is signed with it (x-cryptoinfra-signature, HMAC-SHA256 of the raw body).

#2. Create a user, get their address

bash
curl -sX POST $API/v1/users \
  -H "Authorization: Bearer $CRYPTOINFRA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"external_user_id":"10482"}'
json
{ "user_id": "10482", "addresses": [ { "chain": "tron", "address": "TJYBSfZf…" } ] }

external_user_id is the id this user has in your system: a number, 5 to 10 digits. The address is permanent. Show it to the user once, save it if you like — it never changes. Calling this again with the same id is safe: you get the same user and the same address.

#3. Money arrives — we tell you

When the user sends USDT, USDC or TRX to that address and it confirms, your webhook gets:

json
{
  "userExternalId": "10482",
  "assetId": "usdt_trc20",
  "amount": "<decimal string>",
  "amountCredited": "<decimal string>",
  "txid": "<txid>",
  "fromAddress": "<who sent it, T…>"
}

with header x-cryptoinfra-event: deposit.credited. Deposits are free, so amountCredited equals amount unless you set a fee of your own. Reply 200 fast; do the work after.

#4. Read the balance

bash
curl -s $API/v1/users/10482/balances \
  -H "Authorization: Bearer $CRYPTOINFRA_KEY"
json
{ "balances": [ { "asset_id": "usdt_trc20", "symbol": "USDT", "amount": "<decimal string>" } ] }

This is the number to trust. It is already net of anything in flight.

#5. Quote a payout, then send it

bash
curl -s "$API/v1/withdrawal-fee-quote?asset_id=usdt_trc20&amount=25&user_id=10482&to_address=TAs66Ct…" \
  -H "Authorization: Bearer $CRYPTOINFRA_KEY"
json
{ "total_fee": "<decimal string>", "network_fee": "<decimal string>", "platform_fee": "<decimal string>", "dynamic": true }

Show the user total_fee. It is charged on top of the amount, so the balance must cover amount + total_fee. No number is printed here on purpose: the fee is priced the moment you ask and moves with the network — always read it from the quote, never from an example.

bash
curl -sX POST $API/v1/withdrawals \
  -H "Authorization: Bearer $CRYPTOINFRA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"external_user_id":"10482","asset_id":"usdt_trc20",
       "to_address":"TAs66Ct…","amount":"25","idempotency_key":"payout-8814"}'

You get 202 with "status": "queued". amount is what the recipient receives. idempotency_key is your own order id: reuse it on every retry and you can never pay twice.

#6. Learn how it ended

Your webhook gets withdrawal.confirmed (money delivered) or withdrawal.failed (hold refunded, nothing lost). Before you tell the user it is done, read it once:

bash
curl -s $API/v1/withdrawals/186 \
  -H "Authorization: Bearer $CRYPTOINFRA_KEY"

"status": "confirmed" is the truth. Webhooks are a hint; the read is the answer.

That is the whole integration. Everything below is what people get wrong the first time.

#Five mistakes, and the fix for each

MistakeFix
Matching a deposit to an order by amountGive each order its own user, or keep the order on your side. An address tells you who paid, never what for
Crediting your user from the webhook aloneReply 200, then read GET /v1/users/{id}/balances and credit from that
Retrying a payout with a new idempotency_keySame key, every retry. A timeout that did create the payout returns it, not a second one
Printing a fee from these docsQuote it. Every fee is priced when you ask; the quote says whether it is live
Waiting for a swap webhookThere is none. Poll GET /v1/swap/orders/{id}

#Where next