API · Documentation

API Documentation

Accept deposits, send withdrawals, create invoices and verify signed webhooks across USDT (BEP20/TRC20), BNB and BTC through a single REST API.

Base URLhttps://waltix.io/api

Introduction

The Waltix API lets you accept and send crypto programmatically: create deposit addresses and invoices, initiate withdrawals, fetch fees and process signed webhooks. Every endpoint lives under a single base URL.

Base URL for all requests:

http
https://waltix.io/api

A signature is required for every public endpoint (GET, POST, etc.). The algorithm is described in the “Authentication” section.

Supported networks

USDT on BEP20 and TRC20, BNB (BEP20) and BTC. The full up-to-date list of symbol identifiers is returned by GET /symbol/.

Authentication & keys

Keys

KeyWhere to get itPurpose
API key ID + secretDashboard → API keysSigning API requests
callback_keyDashboard → profileVerifying webhooks

An API key is created once: the secret is shown only at creation time. For webhooks use callback_key, not the API key secret.

Request headers

HeaderDescription
X-API-Key-IDKey UUID
X-TimestampUnix timestamp, seconds
X-SignatureHMAC-SHA256 hex

Signing algorithm

  1. Build an object from params, body and path:
json
{
  "params": "url_encoded_query_string",
  "body": {},
  "path": "/api/user/balance/"
}
  • params — query string as in the URL (search_query=foo, without ?)
  • body — JSON request body; {} if there is no body
  • path — full request path with the /api/ prefix, e.g. /api/user/balance/
  1. Serialize: json.dumps(data, separators=(",", ":"), sort_keys=True)
  2. Compute the signature:
text
signature = HMAC-SHA256(key=API_KEY_SECRET, message=timestamp + json_string)

The timestamp must be within ±15 seconds of server time. If the key allowlist is empty, the first successful request records the current IP into the allowlist, and subsequent requests are accepted only from those IPs.

Example: GET balance

bash
# params пустые, body {}
# path = /api/user/balance/
# data = {"body":{},"params":"","path":"/api/user/balance/"}

curl "https://waltix.io/api/user/balance/" \
  -H "X-API-Key-ID: <UUID>" \
  -H "X-Timestamp: 1718659200" \
  -H "X-Signature: <hex>"

Example: POST address

bash
curl "https://waltix.io/api/user/address/" \
  -X POST \
  -H "Content-Type: application/json" \
  -H "X-API-Key-ID: <UUID>" \
  -H "X-Timestamp: 1718659200" \
  -H "X-Signature: <hex>" \
  -d '{"symbol_id":"USDTBEP20","callback_url":"https://example.com/cb","description":"shop"}'

Python: generating the signature

python
import hashlib, hmac, json, time

def sign(key: str, timestamp: int, data: dict) -> str:
    s = json.dumps(data, separators=(",", ":"), sort_keys=True)
    return hmac.new(key.encode(), f"{timestamp}{s}".encode(), hashlib.sha256).hexdigest()

data = {"params": "", "body": {"symbol_id": "USDTBEP20"}, "path": "/api/user/address/"}
ts = int(time.time())
signature = sign(API_KEY_SECRET, ts, data)

PHP: generating the signature

php
$data = ["params" => "", "body" => ["symbol_id" => "USDTBEP20"], "path" => "/api/user/address/"];
ksort($data);
$json = json_encode($data, JSON_UNESCAPED_SLASHES);
$signature = hash_hmac("sha256", time() . $json, $API_KEY_SECRET);

Webhooks

A POST is delivered to your callback_url. The signature is verified with callback_key using the X-Signature and X-Timestamp headers. Allowed timestamp drift: ±15 seconds.

HeaderDescription
X-SignatureHMAC-SHA256 hex
X-TimestampUnix timestamp

Signing payload

json
{
  "params": "",
  "body": { }
}
  • params — query string of your endpoint
  • body — parsed JSON body of the POST
  • the path field is not used
  • callback_key is used for the signature

Deposit / withdrawal webhook

The POST body carries the transaction JSON object (fields described in the relevant sections). For signature verification the object is placed into body, while params stays empty.

Invoice webhook

json
{
  "params": "",
  "body": {
    "type": "invoice",
    "id": "5ca38db1-00c5-4aba-91e5-6a57bed60aee",
    "status": "executed"
  }
}

The POST body matches the signing object: params and body at the top level. Verify using the parsed request body as-is, without any extra wrapper.

Python: verifying a webhook

python
# Webhook депозита и вывода / Deposit & withdrawal webhook
body = await request.json()
data = {"params": request.url.query, "body": body}
verify(request.headers["X-Signature"], CALLBACK_KEY, int(request.headers["X-Timestamp"]), data)

# Webhook счёта / Invoice webhook
data = await request.json()
verify(request.headers["X-Signature"], CALLBACK_KEY, int(request.headers["X-Timestamp"]), data)
python
def verify(signature: str, key: str, timestamp: int, data: dict) -> bool:
    if abs(int(time.time()) - timestamp) > 15:
        return False
    expected = sign(key, timestamp, data)
    return hmac.compare_digest(expected, signature)

Webhook delivery

If your server responds with an error, the webhook is retried automatically. If your server already returned 2xx for the first webhook, no retry is sent when the status changes to executed. With AML or pending confirmations the first webhook may arrive with status pending. Query the current status via GET /user/transaction/.

Deposits

A deposit is registered after an on-chain transfer to an address obtained via the API. The blockchain scanner creates a transaction with type deposit.

  1. Create an address: POST /user/address/ or an invoice: POST /invoice/.
  2. The payer sends an on-chain transfer to the issued address.
  3. The scanner records the deposit. If callback_url is set, a webhook is sent.

Endpoints

  • POST/user/address/
  • GET/user/address/
  • GET/user/address/balance/{symbol_id}/
  • POST/invoice/
  • GET/invoice/
  • GET/invoice/my/{invoice_id}/
  • GET/invoice/{invoice_id}/transactions/
  • GET/symbol/
  • GET/user/transaction/
  • GET/user/transaction/{encoded_id}/

POST /user/address/ — request

FieldTypeDescription
symbol_id *stringSymbol identifier: currency on a specific network, e.g. USDTBEP20, BNBBEP20. List: GET /symbol/
callback_urlstringURL for the deposit webhook
descriptionstringAddress label
external_idstringYour ID

Virtual symbols (USDT without a network) are not accepted. Specify a network-bound symbol.

POST /user/address/ — response

FieldDescription
idAddress ID
valueWallet address
symbol_idSymbol identifier
callback_urlWebhook URL
external_idYour ID
created_atCreation date

POST /invoice/ — request

FieldTypeDescription
name *stringName
symbol_idstringSymbol identifier for payment
target_value_fiatdecimalAmount in fiat
target_currency_fiatstringFiat currency, default USD
target_value_symboldecimalAmount in crypto
deadline_atdatetimeInvoice deadline
callback_urlstringInvoice status webhook URL
external_idstringYour ID
is_payer_feeboolPayer covers the fee
descriptionstringDescription

You must provide either target_value_fiat or target_value_symbol. Response: payment address in target_wallet.value, current status, and the payment link in pay_url.

GET /user/transaction/ — deposit filters

ParameterDescription
typedeposit
symbol_idSymbol identifier
external_idYour ID
search_querySearch by symbol, hash, addresses

Deposit webhook — body

FieldDescription
typedeposit
statusTransaction status
symbol_idSymbol identifier
amountAmount credited to balance
service_feeService fee
net_amountOn-chain transfer amount (amount + service_fee)
blockchain_data.hashTx hash
blockchain_data.from_addressSender
blockchain_data.to_addressRecipient (your address)
external_idYour ID from the address
invoiceInvoice data if the deposit is for an invoice
aml_orderAML check: status, data.level, data.risk_score (if enabled)
callback_orderLast webhook delivery status (last_status_code, last_response_data)
json
{
  "user_id": 1,
  "type": "deposit",
  "created_at": "2025-11-20T16:08:10.180830+00:00",
  "symbol_id": "USDTBEP20",
  "status": "executed",
  "amount": "4.696700000000000000",
  "service_fee": "0.803300000000000000",
  "net_amount": "5.500000000000000000",
  "blockchain_data": {
    "hash": "0xabc...",
    "from_address": "0x123",
    "to_address": "0x456"
  },
  "external_id": null,
  "callback_url": "https://example.com/cb"
}

Deposit statuses (on-chain / invoice)

statusDescription
pendingAwaiting AML or network confirmations
executedCredited to balance
dirty_lockAddress flagged dirty, deposit locked
cancelledCancelled

If the AML check fails, the transaction status becomes risky. AML fields: aml_order.status (created, pending_check, completed, risky, failed, manual_completed), aml_order.data.level (undefined, none, low, medium, high, severe), aml_order.data.risk_score (0–100).

Invoice statuses

statusDescription
activeAwaiting payment
executedPaid
expiredExpired
cancelledCancelled

Deposits below min_deposit_amount are ignored.

Withdrawals

Endpoints

  • POST/user/balance/withdrawal/
  • GET/user/transaction/?type=withdrawal
  • GET/user/transaction/{encoded_id}/
  • GET/symbol/

There is no separate GET /withdrawal/{id} — use the transaction API.

POST /user/balance/withdrawal/ — query

ParameterDescription
totp_codeEmail OTP. Not required when authenticating with an API key

POST /user/balance/withdrawal/ — request

FieldTypeDescription
symbol_id *stringSymbol identifier, e.g. USDTTRC20. List: GET /symbol/
to_address *stringRecipient address
amount *decimalAmount to recipient (net)
external_idstringYour ID
callback_urlstringWebhook URL
commentstringComment (up to 255 chars)
extra.memostringMemo/tag (if the network supports it)
extra.fee_modestringNetwork fee priority for BTC (see below)

The fee is charged on top of amount: amount + service_fee is deducted from the balance. With API key auth the withdrawal_via_api_* fees apply (see GET /setting/fees/). Rate limit: 5 requests per second.

BTC fee priority (extra.fee_mode)

fee_modeDescription
fastestFeeHighest priority, fastest
economyFeeEconomy mode
minimumFeeMinimum priority (server default)

If fee_mode is omitted or unknown, the server default (economyFee) is used. Per-mode markup is available in GET /blockchain/ → settings.service_fee_markup_by_fee_mode for the BTC network. For networks other than BTC the field is ignored.

Errors

detailReason
Insufficient balanceNot enough funds
Invalid addressInvalid address
Minimum withdrawal amount is …Below minimum
TOTP code is requiredtotp_code required without an API key
MEMO doesn't supported on this chain!Memo is not supported

Withdrawal webhook — body

Sent once when the withdrawal transitions to executed. Transaction JSON object with type: withdrawal.

FieldDescription
amountTransaction amount (recipient + fee)
service_feeFee
net_amountAmount to recipient (amount - service_fee)
statusexecuted
blockchain_data.hashTx hash
to_addressRecipient address
memoMemo, if any
external_idYour ID
fee_modeBTC fee priority, if specified

Withdrawal statuses

Withdrawal status comes from the withdrawal lifecycle. AML statuses (risky etc.) are not used here, unlike deposits. Terminal states: executed, cancelled, failed.

statusDescription
compliance_approve_requiredAwaiting manual approval
createdQueued
aml_order_pendingAML check
prepare_pendingPreparing the transaction
ready_to_transferReady to transfer
transfer_lockBroadcasting to the network
pendingAwaiting confirmations
swap_lockAwaiting autoswap
executedExecuted
cancelledCancelled
failedFailed

Limits

ParameterDescription
min_withdrawal_amountMinimum per symbol and tariff
withdrawal_fee_amountFixed fee (UI / without API key)
withdrawal_fee_percentagePercentage fee (UI / without API key)
withdrawal_via_api_fee_amountFixed fee for API key requests
withdrawal_via_api_fee_percentagePercentage fee for API key requests
totp_codeRequired without an API key (email OTP, 10 min)

Repeating a request with the same external_id is not idempotent.

Fees & pricing

Endpoints

  • GET/setting/fees/
  • GET/setting/fees/my/

GET /setting/fees/ is available without a session, using an API key. The user_id and tariff_id query params are for admin viewing of specific overrides (usually not needed by integrators).

FeesSettingsDTO response fields

FieldDescription
symbol_idSymbol identifier
user_idPer-user override (null — global)
tariff_idPer-tariff override (null — global)
min_deposit_amountMinimum deposit amount
min_withdrawal_amountMinimum withdrawal amount
deposit_fee_amountFixed deposit fee (UI)
deposit_fee_percentageDeposit fee % (UI)
withdrawal_fee_amountFixed withdrawal fee (UI)
withdrawal_fee_percentageWithdrawal fee % (UI)
deposit_via_api_fee_amountFixed deposit fee via API key
deposit_via_api_fee_percentageDeposit fee % via API key
withdrawal_via_api_fee_amountFixed withdrawal fee via API key
withdrawal_via_api_fee_percentageWithdrawal fee % via API key
swap_fee_amountFixed swap fee
swap_fee_percentageSwap fee %
swap_from_min_amountMin. “from” amount for swap
swap_to_min_amountMin. “to” amount for swap

Resolution priority: per-user override → tariff → global settings. For API key integration use the *_via_api_* fields. For dashboard operations use deposit_fee_* / withdrawal_fee_*.

Need help?

Create an API key in your dashboard and start integrating in minutes.

Open dashboard