# 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.

**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

| Mistake | Fix |
| --- | --- |
| Matching a deposit to an order by amount | Give 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 alone | Reply `200`, then read `GET /v1/users/{id}/balances` and credit from that |
| Retrying a payout with a new `idempotency_key` | Same key, every retry. A timeout that did create the payout returns it, not a second one |
| Printing a fee from these docs | Quote it. Every fee is priced when you ask; the quote says whether it is live |
| Waiting for a swap webhook | There is none. Poll `GET /v1/swap/orders/{id}` |

## Where next

- [Static Wallet Address](deposit-addresses) — everything about the address and what lands on it
- [Payouts](payouts) — statuses, batches, what each error means
- [Webhooks](webhooks) — verifying the signature, retries, the full event list
- [Fees and pricing](fees) — the rule and the live price sheet
- [Fee calculator](fee-calculator) — the sheet applied to any amount, live
- [Errors](errors) and [Idempotency](idempotency) — the two conventions worth reading before go-live
