Troubleshooting Guide
This guide covers common integration problems and concrete fixes.
Authentication Errors
Symptom
CryptoBotError: code=401, name=UNAUTHORIZED
Checks
import os
from cryptobot import CryptoBotClient
client = CryptoBotClient(api_token=os.environ["CRYPTOBOT_API_TOKEN"])
print(client.get_me().name)
If that fails:
Confirm token is present and non-empty.
Confirm the token belongs to the intended environment.
If using testnet token, set
is_mainnet=False.
Mainnet vs Testnet Mismatch
import os
from cryptobot import CryptoBotClient
mainnet = CryptoBotClient(api_token=os.environ["CRYPTOBOT_API_TOKEN"], is_mainnet=True)
testnet = CryptoBotClient(api_token=os.environ["CRYPTOBOT_TESTNET_TOKEN"], is_mainnet=False)
Use one token per environment and do not mix them.
Amount Validation Failures
Symptom
CryptoBotError: code=400, name=AMOUNT_TOO_SMALL
Fix
Validate amount before API calls:
from decimal import Decimal
from cryptobot.models import Asset
MIN_AMOUNTS = {
Asset.USDT: Decimal("0.01"),
Asset.USDC: Decimal("0.01"),
Asset.TON: Decimal("0.01"),
Asset.BTC: Decimal("0.000001"),
}
def validate_amount(asset: Asset, amount: float):
value = Decimal(str(amount))
minimum = MIN_AMOUNTS.get(asset)
if minimum is not None and value < minimum:
raise ValueError(f"Amount too small for {asset.name}. Minimum is {minimum}")
Invoice Status Not Updating
If an invoice appears stuck:
Query by invoice ID.
Check for
Status.paidorStatus.expired.Create a replacement invoice if expired.
from cryptobot.models import Asset, Status
def refresh_invoice(client, invoice_id: int):
invoices = client.get_invoices(invoice_ids=str(invoice_id))
return invoices[0] if invoices else None
def renew_if_expired(client, invoice):
if invoice.status != Status.expired:
return invoice
return client.create_invoice(
asset=invoice.asset,
amount=float(invoice.amount),
description=invoice.description,
payload=invoice.payload,
expires_in=3600,
)
Transfer Problems
Insufficient balance
CryptoBotError: code=400, name=INSUFFICIENT_FUNDS
from decimal import Decimal
def has_balance(client, asset, amount: float) -> bool:
required = Decimal(str(amount))
for bal in client.get_balances():
if bal.currency_code == asset.name:
return Decimal(bal.available) >= required
return False
Duplicate spend_id
CryptoBotError: code=400, name=SPEND_ID_ALREADY_USED
from datetime import datetime
import uuid
def unique_spend_id(prefix: str, user_id: int) -> str:
return f"{prefix}_{user_id}_{datetime.utcnow().strftime('%Y%m%d%H%M%S')}_{uuid.uuid4().hex[:8]}"
Timeout and Connection Issues
Increase timeout and enable built-in retry/backoff for transient transport errors:
import os
from cryptobot import CryptoBotClient
from cryptobot.models import Asset
client = CryptoBotClient(
api_token=os.environ["CRYPTOBOT_API_TOKEN"],
timeout=30.0,
max_retries=3,
retry_backoff=0.5,
)
invoice = client.create_invoice(asset=Asset.USDT, amount=5, description="network-safe")
Webhook Signature Failures
Most signature bugs come from verifying parsed JSON instead of the raw body.
import json
import os
from fastapi import FastAPI, HTTPException, Request
from cryptobot.webhook import check_signature
app = FastAPI()
api_token = os.environ["CRYPTOBOT_API_TOKEN"]
@app.post("/webhook")
async def webhook(request: Request):
raw = await request.body()
raw_str = raw.decode("utf-8")
if not check_signature(api_token, raw_str, request.headers):
raise HTTPException(status_code=400, detail="Invalid signature")
data = json.loads(raw_str)
return {"ok": True, "update_type": data.get("update_type")}
Webhooks Not Received
Checklist:
Endpoint is publicly reachable over HTTPS.
Webhook URL in Crypto Bot is correct.
Your service is listening on expected path/port.
Reverse proxy forwards request body and headers unchanged.
For local tests, run your listener script and expose it with ngrok:
ngrok http 2203
Enum Parsing Errors
When converting user input to enums, normalize and validate:
from cryptobot.models import Asset, Status
def parse_asset(text: str) -> Asset:
try:
return Asset[text.upper()]
except KeyError as exc:
raise ValueError(f"Unsupported asset: {text}") from exc
def parse_status(text: str) -> Status:
try:
return Status[text.lower()]
except KeyError as exc:
raise ValueError(f"Unsupported status: {text}") from exc
Rate Limit Errors
Symptom
CryptoBotError: code=429, name=TOO_MANY_REQUESTS
Fix
import os
from cryptobot import CryptoBotClient
client = CryptoBotClient(
api_token=os.environ["CRYPTOBOT_API_TOKEN"],
max_retries=4,
retry_backoff=0.5,
retryable_status_codes={429},
)
app = client.get_me()
print(app.name)
Testnet Smoke Test
import os
from cryptobot import CryptoBotClient
from cryptobot.models import Asset
def smoke_test() -> bool:
client = CryptoBotClient(
api_token=os.environ["CRYPTOBOT_TESTNET_TOKEN"],
is_mainnet=False,
)
try:
client.get_me()
client.get_balances()
client.get_exchange_rates()
client.create_invoice(asset=Asset.USDT, amount=1, description="smoke")
return True
except Exception:
return False