Tyxo Pay API
Принимайте оплату в криптовалюте прямо в Telegram. Создайте счёт, отправьте покупателю ссылку, а когда он оплатит в боте @tyxobot, мы пришлём вебхук на ваш сервер. Деньги сразу падают на баланс владельца приложения.
Быстрый старт
- Откройте @tyxobot, затем мини-апп: Сервисы → Для бизнеса → Новое приложение.
- Сохраните токен. Он показывается один раз, у нас хранится только его хеш. Потеряли или засветили токен? Выпустите новый в настройках приложения, старый сразу перестанет работать.
- Укажите адрес вебхука, если хотите получать уведомления об оплате.
- Сделайте первый запрос:
curl https://api.tyxo.app/v1/getMe \
-H "Tyxo-Api-Token: ВАШ_ТОКЕН"
const response = await fetch('https://api.tyxo.app/v1/getMe', {
headers: { 'Tyxo-Api-Token': process.env.TYXO_TOKEN },
});
const { ok, result, error } = await response.json();
import os, requests
response = requests.get(
"https://api.tyxo.app/v1/getMe",
headers={"Tyxo-Api-Token": os.environ["TYXO_TOKEN"]},
)
print(response.json())
Запросы и ответы
Каждый метод вызывается по адресу https://api.tyxo.app/v1/<метод>. Токен передаётся в заголовке
Tyxo-Api-Token. Параметры можно передать в строке запроса (GET) или телом JSON (POST,
Content-Type: application/json), можно и смешать. Тело не больше 64 КБ.
Все суммы это строки с десятичной дробью, например "12.5". Так не теряется точность: у
GRAM 9 знаков после запятой, у USDT 6. Больше знаков, чем у актива, передать нельзя.
Успешный ответ всегда HTTP 200:
{ "ok": true, "result": { ... } }
Ошибка приходит с HTTP-кодом из error.code и машинным именем в error.name:
{ "ok": false, "error": { "code": 400, "name": "AMOUNT_INVALID" } }
Активы
В параметре asset передаётся код актива, регистр не важен. Монета сети TON в приложении называется
GRAM (ex. TON), а в API у неё код TON.
| Код | Название | Знаков после запятой |
|---|---|---|
TON | GRAM (ex. TON), нативная монета сети | 9 |
USDT | Tether USD | 6 |
XAUT | Tether Gold | 6 |
NOT | Notcoin | 9 |
DOGS | DOGS | 9 |
HMSTR | Hamster Kombat | 9 |
CATI | Catizen | 9 |
MAJOR | Major | 9 |
STON | STON.fi | 9 |
TSTON | Tonstakers TON | 9 |
Комиссии и лимиты
- С оплаченного счёта удерживается 0.5%. Точная сумма комиссии видна в поле
feeсчёта сразу при создании. - Выплаты через
transferвнутри Tyxo бесплатные. - До 10 приложений на один аккаунт.
- Описание счёта до 200 символов,
payloadдо 4096 символов,spend_idдо 64 символов.
getMe
GETPOSTПроверяет токен и возвращает данные приложения. Параметров нет.
{
"app_id": "6c1f6c1e-6c4e-4c79-9d63-1f2a0b7d9c11",
"name": "Мой магазин",
"webhook_url": "https://shop.example/tyxo",
"transfers_enabled": false
}
getBalance
GETPOSTБаланс владельца приложения по всем активам. Параметров нет.
[
{ "asset": "TON", "available": "128.4512" },
{ "asset": "USDT", "available": "214.5" }
]
createInvoice
POSTGET
Создаёт счёт и возвращает Invoice. Отправьте покупателю pay_url: он откроет
бота и оплатит с баланса Tyxo.
| Параметр | Описание | |
|---|---|---|
asset | обязателен | Код актива из таблицы. |
amount | обязателен | Сумма строкой, больше нуля. |
description | необязателен | Что оплачивают. Покупатель видит это в боте. До 200 символов. |
payload | необязателен | Любые ваши данные, например номер заказа или JSON. Покупатель их не видит, они вернутся в счёте и вебхуке. До 4096 символов. |
curl https://api.tyxo.app/v1/createInvoice \
-H "Tyxo-Api-Token: ВАШ_ТОКЕН" \
-H "Content-Type: application/json" \
-d '{"asset":"USDT","amount":"25","description":"Заказ №42","payload":"order-42"}'
const response = await fetch('https://api.tyxo.app/v1/createInvoice', {
method: 'POST',
headers: { 'Tyxo-Api-Token': process.env.TYXO_TOKEN, 'Content-Type': 'application/json' },
body: JSON.stringify({ asset: 'USDT', amount: '25', description: 'Заказ №42', payload: 'order-42' }),
});
const { result: invoice } = await response.json();
console.log(invoice.pay_url);
import os, requests
invoice = requests.post(
"https://api.tyxo.app/v1/createInvoice",
headers={"Tyxo-Api-Token": os.environ["TYXO_TOKEN"]},
json={"asset": "USDT", "amount": "25", "description": "Заказ №42", "payload": "order-42"},
).json()["result"]
print(invoice["pay_url"])
getInvoices
GETPOSTСчета приложения, сначала новые. Возвращает { "items": [Invoice] }.
| Параметр | Описание | |
|---|---|---|
status | необязателен | active, paid или cancelled. |
invoice_ids | необязателен | Идентификаторы счетов через запятую. |
offset | необязателен | Сколько счетов пропустить. По умолчанию 0. |
count | необязателен | Сколько вернуть, от 1 до 1000. По умолчанию 100. |
deleteInvoice
POSTGET
Отменяет активный счёт, после этого его нельзя оплатить. Оплаченный счёт отменить нельзя, придёт
INVOICE_NOT_ACTIVE. Возвращает true.
| Параметр | Описание | |
|---|---|---|
invoice_id | обязателен | Идентификатор счёта. |
transfer
POSTGETПереводит деньги с баланса владельца пользователю Tyxo по его Telegram ID. Подходит для выплат, кэшбэка и выигрышей. Пользователь получит уведомление в боте.
TRANSFERS_DISABLED.
| Параметр | Описание | |
|---|---|---|
user_id | обязателен | Telegram ID получателя. Он должен хотя бы раз запустить @tyxobot. |
asset | обязателен | Код актива. |
amount | обязателен | Сумма строкой. |
spend_id |
обязателен |
Ваш уникальный ключ выплаты, до 64 символов. Повторный запрос с тем же spend_id вернёт ту же выплату и не
спишет деньги второй раз, так что запрос безопасно повторять при сетевых ошибках. Тот же ключ с другими параметрами
вернёт SPEND_ID_REUSED.
|
{
"transfer_id": "f3b0c2a4-1d2e-4c5b-9a8f-6e7d5c4b3a21",
"user_id": "123456789",
"asset": "USDT",
"amount": "5",
"spend_id": "cashback-2026-10-15-77"
}
Объект Invoice
| Поле | Тип | Описание |
|---|---|---|
invoice_id | string | Идентификатор счёта (UUID). |
hash | string | Короткий код счёта, из него собрана ссылка. |
status | string | active, paid или cancelled. |
asset | string | Код актива. |
amount | string | Сумма, которую платит покупатель. |
fee | string | Комиссия Tyxo. На баланс приходит amount минус fee. |
description | string | null | Описание для покупателя. |
payload | string | null | Ваши данные из createInvoice. |
pay_url | string | Ссылка на оплату в боте. |
created_at | string | Время создания, ISO 8601 в UTC. |
paid_at | string | null | Время оплаты, если оплачен. |
{
"invoice_id": "0b6f3e2a-8c1d-4f5e-9a7b-2c3d4e5f6a7b",
"hash": "Vj3kQ9xZp2LmN7aB4cD8eF",
"status": "active",
"asset": "USDT",
"amount": "25",
"fee": "0.125",
"description": "Заказ №42",
"payload": "order-42",
"pay_url": "https://t.me/tyxobot?start=i_Vj3kQ9xZp2LmN7aB4cD8eF",
"created_at": "2026-10-15T12:00:00.000Z",
"paid_at": null
}
Вебхуки
Когда счёт оплачен, мы отправляем POST с JSON на адрес вебхука приложения. Адрес задаётся в настройках
приложения в мини-аппе и должен быть публичным https с доменом, IP-адреса не принимаются.
{
"update_id": 1042,
"update_type": "invoice_paid",
"request_date": "2026-10-15T12:03:41.512Z",
"payload": { Invoice со статусом "paid" }
}
- Ответьте любым кодом 2xx в течение 10 секунд, иначе доставка считается неудачной. Редиректы не выполняются.
- Неудачную доставку повторяем: первая попытка через 30 секунд, дальше пауза удваивается до часа. Попытки идут 3 дня.
- Одно и то же событие может прийти повторно, если ваш ответ потерялся в сети. Обрабатывайте
update_idодин раз. - Перед тем как выдавать товар, проверьте подпись запроса.
Проверка подписи
Каждый вебхук подписан. В заголовке Tyxo-Api-Signature лежит HMAC-SHA256 от сырого тела
запроса в hex. Ключ HMAC это SHA-256 от токена приложения (32 байта, не hex-строка). Считайте подпись по байтам
тела как они пришли, до разбора JSON.
import { createHash, createHmac, timingSafeEqual } from 'node:crypto';
export function isSignedByTyxo(rawBody, signature, token) {
const key = createHash('sha256').update(token).digest();
const expected = createHmac('sha256', key).update(rawBody).digest('hex');
return typeof signature === 'string'
&& signature.length === expected.length
&& timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}
app.post('/tyxo', express.raw({ type: 'application/json' }), (request, response) => {
if (!isSignedByTyxo(request.body, request.get('tyxo-api-signature'), process.env.TYXO_TOKEN)) {
return response.sendStatus(401);
}
const update = JSON.parse(request.body);
response.sendStatus(200);
});
import hashlib, hmac, os
def is_signed_by_tyxo(raw_body: bytes, signature: str, token: str) -> bool:
key = hashlib.sha256(token.encode()).digest()
expected = hmac.new(key, raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature or "")
@app.post("/tyxo")
def tyxo_webhook():
if not is_signed_by_tyxo(request.get_data(), request.headers.get("Tyxo-Api-Signature"), os.environ["TYXO_TOKEN"]):
return "", 401
update = request.get_json()
return "", 200
amount и asset с заказом. При сомнениях
запросите счёт через getInvoices по invoice_ids.
Ошибки
| name | HTTP | Что случилось |
|---|---|---|
UNAUTHORIZED | 401 | Нет токена или он неверный, отозван. |
METHOD_NOT_FOUND | 404 | Такого метода нет. |
BODY_INVALID | 400 | Тело не JSON-объект. |
BODY_TOO_LARGE | 413 | Тело больше 64 КБ. |
ASSET_REQUIRED | 400 | Не передан asset. |
ASSET_INVALID | 400 | Такого актива нет. |
AMOUNT_REQUIRED | 400 | Не передан amount. |
AMOUNT_INVALID | 400 | Сумма не число, не больше нуля или с лишними знаками после запятой. |
DESCRIPTION_TOO_LONG | 400 | Описание длиннее 200 символов. |
PAYLOAD_TOO_LONG | 400 | payload длиннее 4096 символов. |
STATUS_INVALID | 400 | Неизвестный status в фильтре. |
PAGINATION_INVALID | 400 | offset или count вне допустимых значений. |
INVOICE_IDS_INVALID | 400 | В invoice_ids есть не UUID. |
INVOICE_ID_INVALID | 400 | invoice_id не передан или не UUID. |
INVOICE_NOT_FOUND | 404 | Счёта нет или он принадлежит другому приложению. |
INVOICE_NOT_ACTIVE | 409 | Счёт уже оплачен или отменён. |
TRANSFERS_DISABLED | 403 | Выплаты через API выключены в настройках приложения. |
USER_ID_REQUIRED | 400 | Не передан user_id. |
USER_NOT_FOUND | 400 | Пользователь ни разу не запускал бота. |
USER_IS_APP_OWNER | 400 | Нельзя перевести самому себе. |
SPEND_ID_REQUIRED | 400 | Не передан spend_id. |
SPEND_ID_TOO_LONG | 400 | spend_id длиннее 64 символов. |
SPEND_ID_REUSED | 409 | Этот spend_id уже использован для другой выплаты. |
INSUFFICIENT_FUNDS | 400 | На балансе владельца не хватает средств. |
INTERNAL_ERROR | 500 | Сбой на нашей стороне. Запрос можно повторить. |