Telegram Stars Bot API Guide: sendInvoice, XTR, Webhooks, Subscriptions and Refunds
Everything a developer needs to accept Telegram Stars in a bot or Mini App: the XTR currency, invoice creation, the pre-checkout handshake, successful_payment handling, Stars subscriptions, refunds, transaction history, and the pitfalls that break payments in production.
Key takeaways
- Stars payments use the standard Bot Payments API with currency "XTR" and an empty provider_token; prices are a single LabeledPrice whose amount is the number of Stars.
- You must answer every pre_checkout_query within 10 seconds with answerPreCheckoutQuery, otherwise the payment fails.
- Persist telegram_payment_charge_id from successful_payment: it is required for refundStarPayment and for reconciliation with getStarTransactions.
- Subscriptions are created by passing subscription_period=2592000 (30 days) to createInvoiceLink; Mini Apps open invoices with WebApp.openInvoice.
- If you would rather not build and operate all of this, Telegram Stars Payment Platform exposes a REST API and webhooks on top of a ready-made ledger and payout flow.
Prerequisites
- A bot token from @BotFather. No payment provider needs to be connected for Stars.
- A server (or serverless function) able to receive updates via webhook or long polling.
- Any Bot API library:
python-telegram-bot,aiogram,grammY,Telegraf,node-telegram-bot-api, or raw HTTP calls.
1. Create a Stars invoice
Two methods create invoices. sendInvoice posts the invoice into a chat; createInvoiceLink returns a URL you can share anywhere or open from a Mini App. The parameters that matter for Stars:
| Parameter | Value for Stars |
|---|---|
currency | "XTR" |
provider_token | Empty string (omit or "") |
prices | Exactly one LabeledPrice; amount is the number of Stars (no cents) |
payload | Your internal order ID (1–128 bytes). Never trust it blindly; verify server-side |
title / description | Shown on the payment sheet (title ≤ 32 chars, description ≤ 255) |
photo_url | Optional product image |
Example: createInvoiceLink with raw HTTP
POST https://api.telegram.org/bot<TOKEN>/createInvoiceLink
Content-Type: application/json
{
"title": "Premium Access",
"description": "30 days of premium content",
"payload": "order_7f3a9c",
"currency": "XTR",
"prices": [{ "label": "Premium Access", "amount": 250 }]
}
Example: sendInvoice with python-telegram-bot
from telegram import LabeledPrice
await context.bot.send_invoice(
chat_id=update.effective_chat.id,
title="Premium Access",
description="30 days of premium content",
payload="order_7f3a9c",
provider_token="", # empty for Telegram Stars
currency="XTR",
prices=[LabeledPrice("Premium Access", 250)],
)
Example: createInvoiceLink with grammY (Node.js)
const link = await bot.api.createInvoiceLink(
"Premium Access",
"30 days of premium content",
"order_7f3a9c",
"", // provider_token: empty for Stars
"XTR",
[{ label: "Premium Access", amount: 250 }],
);
// share `link` or open it from a Mini App
2. Answer the pre-checkout query
When the user taps Pay, Telegram sends your bot an update containing pre_checkout_query. This is your last chance to validate the order (stock, user eligibility, payload integrity). You must reply within 10 seconds:
@router.pre_checkout_query()
async def on_pre_checkout(q: PreCheckoutQuery):
if not order_exists(q.invoice_payload):
await q.answer(ok=False, error_message="Order not found")
return
await q.answer(ok=True)
3. Handle successful_payment
After a successful charge Telegram sends a service message with the successful_payment field. Key fields:
currency("XTR") andtotal_amount(Stars).invoice_payload: your order ID.telegram_payment_charge_id: store this; it identifies the transaction for refunds and reconciliation.is_recurring,is_first_recurring,subscription_expiration_datefor subscriptions.
bot.on("message:successful_payment", async (ctx) => {
const sp = ctx.message.successful_payment;
await db.orders.markPaid(sp.invoice_payload, {
chargeId: sp.telegram_payment_charge_id,
stars: sp.total_amount,
userId: ctx.from.id,
});
await deliverProduct(ctx.from.id, sp.invoice_payload);
});
Make the handler idempotent: Telegram may redeliver updates, so check whether the order is already fulfilled before delivering twice.
4. Stars subscriptions
Recurring billing is built in. Pass subscription_period to createInvoiceLink; the only supported value is 2592000 seconds (30 days). Telegram charges the user automatically each period and sends a new successful_payment with is_recurring=true. Use editUserStarSubscription to cancel or re-enable a subscription on the user's behalf. Users can also manage subscriptions from their Telegram settings.
const link = await bot.api.createInvoiceLink(
"Premium Monthly",
"Renews every 30 days",
"sub_user_42",
"",
"XTR",
[{ label: "Premium Monthly", amount: 150 }],
{ subscription_period: 2592000 },
);
5. Refunds
Refund any Stars payment with refundStarPayment(user_id, telegram_payment_charge_id). The Stars return to the user and the transaction is reversed in your Stars balance. Refunds are possible even after delivery, so define a clear policy.
await bot.api.refundStarPayment(userId, chargeId);
6. Reconciliation with getStarTransactions
getStarTransactions(offset, limit) returns the bot's incoming and outgoing Stars transactions (payments, refunds, withdrawals). Run it periodically and compare it with your database; discrepancies usually mean a missed update. getMyStarBalance returns the current Stars balance of the bot.
7. Stars payments from a Mini App
In a Mini App, request an invoice link from your backend, then open it from the front end:
const url = await fetch("/api/invoice", { method: "POST" }).then(r => r.json());
window.Telegram.WebApp.openInvoice(url, (status) => {
if (status === "paid") showSuccess();
else if (status === "cancelled") showCancelled();
else showFailed();
});
The successful_payment update still arrives at your bot; treat it as the source of truth, not the client callback.
8. Withdrawing the Stars your bot earns
Stars accumulate in the bot's balance and become withdrawable after 21 days. Withdraw to TON through Fragment (minimum 1,000 Stars) or spend them on Telegram Ads. See How to withdraw Telegram Stars to TON.
Common pitfalls
- Non-empty
provider_tokenwithXTR→Bad Request. Leave it empty. - Multiple price items → rejected. Stars invoices accept exactly one
LabeledPrice. - Slow pre-checkout handler → payment fails after 10 seconds. Validate locally.
- Not storing
telegram_payment_charge_id→ you cannot refund later. - Trusting the Mini App callback instead of the
successful_paymentupdate → fraud risk. - Non-idempotent delivery → duplicate fulfilment on redelivered updates.
Build or buy?
The Bot API gives you full control, but a production-grade Stars business also needs a ledger, maturity tracking, payout management, refunds UI, analytics and multi-user support. Telegram Stars Payment Platform provides all of this out of the box, with a REST API (create invoices, list payments, request withdrawals) and webhooks for payment events, for a flat 3% fee. Many developers use it as their payment back office while keeping their own bot for the user-facing experience. See how it works.
Frequently asked questions
What currency code do I use for Telegram Stars in the Bot API?
Use "XTR" as the currency and leave provider_token empty. Prices must contain exactly one LabeledPrice whose amount is the number of Stars.
Can a Telegram bot charge in Stars without a payment provider?
Yes. Stars are processed by Telegram itself; no Stripe or other provider token is needed.
How do I refund a Telegram Stars payment?
Call refundStarPayment with the user_id and the telegram_payment_charge_id from the successful_payment message.
How do Telegram Stars subscriptions work in the API?
Create an invoice link with subscription_period set to 2592000 (30 days). Telegram renews automatically and sends a successful_payment with is_recurring=true for each period.
Can I test Telegram Stars payments?
Use the Telegram test environment (test DC) with a bot created there; Stars in the test environment are free. In production, start with a low-priced invoice and refund it.
Ready to accept Telegram Stars?
Create a free account, generate a payment link and start receiving Stars today. No code, no monthly fee, 3% per transaction. Get started or see how it works.
Related articles
Telegram Stars Payment Links: Create One in 60 Seconds Without Coding
What a Telegram Stars payment link is, how it works, how to create one without a bot or code, and how to track clicks and conversions with short redirect links.
Telegram Stars Refunds, the 21-Day Hold and Payout Rules Every Seller Should Know
Why Telegram holds Stars for 21 days, how refunds work for sellers and buyers, minimum payout amounts, and how to manage cash flow when selling with Telegram Stars.
How to Accept Telegram Stars Payments: Step-by-Step Guide for Creators and Merchants
Learn how to accept Telegram Stars payments in minutes: with a no-code payment link, through your own bot, or in a Mini App. Fees, payouts to TON and best practices.