> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.walletstech.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.walletstech.com/_mcp/server.

# Quickstart

## 0. Point at your endpoint

**Your base URL is issued to you.** Every client gets its own endpoint — there is no shared public host — so the first thing you need from your service operator is three values: the **base URL**, a **client id** and an **api key**.

```bash
export BASE_URL="https://<the-endpoint-you-were-issued>"
export CLIENT_ID="<your client id>"
export API_KEY="<your api key>"
```

Keep all three in configuration or a secret store — never hard-coded in source, never in a public repo. Every example below reads them from the environment.

Only the host differs between clients. The paths, request bodies, headers and responses in these docs are identical on every deployment, so nothing else in your integration changes.

## 1. Get a token

Your client id and api key go in **headers**, not the body. There is no request body.

```bash
curl -X POST "$BASE_URL/auth" \
  -H "x-client-id: $CLIENT_ID" \
  -H "x-api-key: $API_KEY"
```

```json
{
  "result": {
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "expiresAt": "2026-07-12T10:15:00.000Z"
  },
  "requestId": "b7a1f0c2-3d4e-4a5b-9c6d-7e8f90a1b2c3"
}
```

The token lasts **15 minutes**. Every call below sends it as `Authorization: Bearer <token>`.

## 2. Create a user

One call mints the user and derives a wallet on every configured chain.

```bash
curl -X POST "$BASE_URL/create" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "password": "a-strong-password" }'
```

```json
{
  "result": {
    "userId": "66b1f0c23d4e4a5b9c6d7e8f",
    "mnemonic": "<your 12-word seed phrase — returned once, never again>",
    "addresses": {
      "BITCOIN": "bc1q5psk2q8t4x495ap75zghxmyvlmk59lr2u93r6j",
      "ETHEREUM": "0xc0c548339ee2af89c078200cabd1b7c7b47d911a",
      "BNB": "0xc0c548339ee2af89c078200cabd1b7c7b47d911a",
      "TRON": "THxvJmtZghePy5XqDE7HKawvFAsAgxcWZt",
      "POLYGON": "0xc0c548339ee2af89c078200cabd1b7c7b47d911a",
      "SOLANA": "EB81pobCpSX16xKeuwYyNMDuZEcpWuHcmimQ9LbdT63d"
    }
  },
  "requestId": "b7a1f0c2-3d4e-4a5b-9c6d-7e8f90a1b2c3"
}
```

**Save the `mnemonic` now.** It is returned exactly once and never stored in plaintext. There is no way to retrieve it later — `/seed` is permanently disabled. Save the `password` too: it decrypts the keys and is required to send funds.

The `password` must be at least 8 characters. The three EVM chains deliberately share one address — it is one account used on Ethereum, BNB Chain and Polygon.

### Importing an existing seed

Pass `existingSeed` instead of letting the service generate one, and `/create` derives the wallets from your mnemonic. If those wallets already exist, it **adopts the existing user** rather than failing — and in that case the response contains **no `mnemonic` field at all** (you already have it).

`mnemonic` is therefore **conditional**. Code like `result.mnemonic.split(' ')` will crash on an adopted user. Always null-check it.

## 3. Check a balance

`assetId` picks the chain and the asset. `21` is USDT on Ethereum — see [Asset IDs](/get-started/assets).

```bash
curl -X POST "$BASE_URL/balance" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "userId": "66b1f0c23d4e4a5b9c6d7e8f", "assetId": 21 }'
```

```json
{ "result": "250.5", "requestId": "b7a1f0c2-3d4e-4a5b-9c6d-7e8f90a1b2c3" }
```

Dust is floored to `"0"`: below 0.00001 of a native coin (0.0001 for BTC), and below 0.0001 of a token. So a `"0"` balance does not prove the address is empty.

## 4. Estimate the fee

`/fee` is the one endpoint that takes **raw addresses instead of a `userId`** — it never touches a wallet.

```bash
curl -X POST "$BASE_URL/fee" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "assetId": 21,
    "sender": "0xc0c548339ee2af89c078200cabd1b7c7b47d911a",
    "recipient": "0x1cc5ca781f2f9ee13eb89283f71abaa24bb61190"
  }'
```

```json
{ "result": "0.000315", "requestId": "b7a1f0c2-3d4e-4a5b-9c6d-7e8f90a1b2c3" }
```

The fee is denominated in the chain's **native** asset, not the asset you are sending. Sending USDT on Ethereum costs ETH — so a token wallet still needs a native balance for gas.

**On a native send, this fee comes out of the `amount`.** Send `"0.5"` ETH and the recipient receives `0.5 ETH − fee`; exactly `0.5 ETH` leaves the wallet. If the recipient must receive an exact figure, add the fee quoted here to the `amount` you send.

Token transfers behave the other way round: the recipient gets the **full** `amount`, and the fee is charged separately in the native coin.

Tron is the exception. It does not return a currency amount, but the **resources** the transfer will consume: `{ "bandwidth": 345, "energy": 130285 }`. Whatever your account does not already cover with free or staked resources is burned as TRX.

## 5. Send

```bash
curl -X POST "$BASE_URL/send" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "userId": "66b1f0c23d4e4a5b9c6d7e8f",
    "assetId": 21,
    "recipient": "0x1cc5ca781f2f9ee13eb89283f71abaa24bb61190",
    "amount": "25.5",
    "password": "a-strong-password"
  }'
```

```json
{
  "result": { "txHash": "0x614719f0004870115fbb95130e0d1faa6df6c19f28298facb1135701326d2e1e" },
  "requestId": "b7a1f0c2-3d4e-4a5b-9c6d-7e8f90a1b2c3"
}
```

**A `txHash` is not a confirmation.** It means the network accepted the transaction, not that it succeeded. Poll `/txid` until `status` is `success` or `failed`.

## 6. Confirm it landed

```bash
curl -X POST "$BASE_URL/txid" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "userId": "66b1f0c23d4e4a5b9c6d7e8f",
    "assetId": 21,
    "txHash": "0x614719f0004870115fbb95130e0d1faa6df6c19f28298facb1135701326d2e1e"
  }'
```

```json
{
  "result": {
    "hash": "0x614719f0004870115fbb95130e0d1faa6df6c19f28298facb1135701326d2e1e",
    "type": "send",
    "amount": "25.5",
    "fee": "0.000315",
    "status": "success",
    "date": "2026-07-11T09:14:22.000Z"
  },
  "requestId": "b7a1f0c2-3d4e-4a5b-9c6d-7e8f90a1b2c3"
}
```

While the transaction is unconfirmed, `status` is `pending` and `date` is `null`. Every chain is normalised to this same row shape, so you write the polling loop once and it works everywhere.

**Polling is the only mechanism there is.** This API has no webhooks and no callbacks — it will never call you, including when a user *receives* funds. Read [Reliability](/get-started/reliability) before you design around it.

## Where to go next

* **[Authentication](/get-started/authentication)** — the token lifecycle, and API credentials vs the wallet password.
* **[Asset IDs](/get-started/assets)** — the integer for every chain and token, plus minimums and dust.
* **[Reliability](/get-started/reliability)** — no webhooks, no pagination, no idempotency key, and how to retry a `/send` without sending twice.
* **[Errors](/get-started/errors)** — the error envelope and what each status means.
* **API Reference** — every endpoint, with a request and response example per asset.