sendInvoice
Use this method to send invoices. On success, the sent Message is returned.
Parameters
chat_idIntegerStringRequiredUnique identifier for the target chat or username of the target channel (in the format
@channelusername)message_thread_idIntegerOptionalUnique identifier for the target message thread (topic) of a forum; for forum supergroups and private chats of bots with forum topic mode enabled only
direct_messages_topic_idIntegerOptionalIdentifier of the direct messages topic to which the message will be sent; required if the message is sent to a direct messages chat
titleStringRequiredminLen 1maxLen 32Product name, 1-32 characters
descriptionStringRequiredminLen 1maxLen 255Product description, 1-255 characters
payloadStringRequiredBot-defined invoice payload, 1-128 bytes. This will not be displayed to the user, use it for your internal processes.
provider_tokenStringOptionalPayment provider token, obtained via @BotFather. Pass an empty string for payments in Telegram Stars.
Three-letter ISO 4217 currency code, see more on currencies. Pass "XTR" for payments in Telegram Stars.
Price breakdown, a JSON-serialized list of components (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.). Must contain exactly one item for payments in Telegram Stars.
max_tip_amountIntegerOptional Default: 0The maximum accepted amount for tips in the smallest units of the currency (integer, not float/double). For example, for a maximum tip of
US$ 1.45 pass maxtipamount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies). Defaults to 0. Not supported for payments in Telegram Stars.suggested_tip_amountsInteger[]OptionalA JSON-serialized array of suggested amounts of tips in the smallest units of the currency (integer, not float/double). At most 4 suggested tip amounts can be specified. The suggested tip amounts must be positive, passed in a strictly increased order and must not exceed max\tip\amount.
start_parameterStringOptionalUnique deep-linking parameter. If left empty, forwarded copies of the sent message will have a Pay button, allowing multiple users to pay directly from the forwarded message, using the same invoice. If non-empty, forwarded copies of the sent message will have a URL button with a deep link to the bot (instead of a Pay button), with the value used as the start parameter
provider_dataStringOptionalJSON-serialized data about the invoice, which will be shared with the payment provider. A detailed description of required fields should be provided by the payment provider.
photo_urlStringOptionalURL of the product photo for the invoice. Can be a photo of the goods or a marketing image for a service. People like it better when they see what they are paying for.
photo_sizeIntegerOptionalPhoto size in bytes
photo_widthIntegerOptionalPhoto width
photo_heightIntegerOptionalPhoto height
need_nameBooleanOptionalPass True if you require the user's full name to complete the order. Ignored for payments in Telegram Stars.
need_phone_numberBooleanOptionalPass True if you require the user's phone number to complete the order. Ignored for payments in Telegram Stars.
need_emailBooleanOptionalPass True if you require the user's email address to complete the order. Ignored for payments in Telegram Stars.
need_shipping_addressBooleanOptionalPass True if you require the user's shipping address to complete the order. Ignored for payments in Telegram Stars.
send_phone_number_to_providerBooleanOptionalPass True if the user's phone number should be sent to the provider. Ignored for payments in Telegram Stars.
send_email_to_providerBooleanOptionalPass True if the user's email address should be sent to the provider. Ignored for payments in Telegram Stars.
is_flexibleBooleanOptionalPass True if the final price depends on the shipping method. Ignored for payments in Telegram Stars.
disable_notificationBooleanOptionalSends the message silently. Users will receive a notification with no sound.
protect_contentBooleanOptionalProtects the contents of the sent message from forwarding and saving
allow_paid_broadcastBooleanOptionalPass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance
message_effect_idStringOptionalUnique identifier of the message effect to be added to the message; for private chats only
A JSON-serialized object containing the parameters of the suggested post to send; for direct messages chats only. If the message is sent as a reply to another suggested post, then that suggested post is automatically declined.
Description of the message to reply to
A JSON-serialized object for an inline keyboard. If empty, one 'Pay
total price' button will be shown. If not empty, the first button must be a Pay button.Returns
On success, the Message object is returned.
GramIO Usage
ts
// Send a Telegram Stars invoice (no provider_token needed)
bot.command("buy", (ctx) =>
ctx.sendInvoice({
title: "Premium Access",
description: "Unlock all premium features for 30 days",
payload: "premium_30d",
currency: "XTR",
prices: [{ label: "Premium (30 days)", amount: 100 }],
})
);ts
// Send an invoice with fiat currency and tipping options
bot.command("donate", (ctx) =>
ctx.sendInvoice({
title: "Support the project",
description: "Help us keep the bot running",
payload: `donation_${ctx.from?.id}`,
provider_token: "PROVIDER_TOKEN_FROM_BOTFATHER",
currency: "USD",
// Prices are in smallest units: 100 = $1.00
prices: [{ label: "Donation", amount: 500 }],
max_tip_amount: 1000,
suggested_tip_amounts: [100, 300, 500, 1000],
})
);ts
// Handle pre-checkout: MUST answer within 10 seconds
bot.on("pre_checkout_query", async (ctx) => {
// Validate the order (check stock, verify payload, etc.)
const isValid = ctx.payload.invoice_payload.startsWith("premium_");
if (isValid) {
await bot.api.answerPreCheckoutQuery({
pre_checkout_query_id: ctx.payload.id,
ok: true,
});
} else {
await bot.api.answerPreCheckoutQuery({
pre_checkout_query_id: ctx.payload.id,
ok: false,
error_message: "This product is no longer available.",
});
}
});ts
// Handle successful payment
bot.on("message", (ctx) => {
if (!ctx.payload.successful_payment) return;
const payment = ctx.payload.successful_payment;
// payment.telegram_payment_charge_id — save for refund support
// payment.invoice_payload — your bot-defined identifier
return ctx.send(`✅ Payment received! Order: ${payment.invoice_payload}`);
});ts
// Direct API call — send invoice to a channel
await bot.api.sendInvoice({
chat_id: "@mychannel",
title: "Channel Subscription",
description: "Support the channel with a Stars donation",
payload: "channel_sub",
currency: "XTR",
prices: [{ label: "Subscription", amount: 50 }],
});Errors
| Code | Error | Cause |
|---|---|---|
| 400 | Bad Request: chat not found | chat_id is invalid or the bot has no access |
| 400 | Bad Request: CURRENCY_INVALID | currency code is not in the supported list |
| 400 | Bad Request: INVOICE_PAYLOAD_INVALID | payload is empty or exceeds 128 bytes |
| 400 | Bad Request: prices must be non-empty | prices array is empty — at least one LabeledPrice is required |
| 400 | Bad Request: XTR invoices must have exactly one price | For currency: "XTR", the prices array must contain exactly one item |
| 400 | Bad Request: provider_token is required | Non-XTR currencies need a provider_token from BotFather |
| 400 | Bad Request: TITLE_INVALID | title is empty or longer than 32 characters |
| 400 | Bad Request: DESCRIPTION_INVALID | description is empty or longer than 255 characters |
| 403 | Forbidden: bot was blocked by the user | User blocked the bot — catch and mark as inactive |
| 429 | Too Many Requests: retry after N | Rate limit hit — check retry_after, use auto-retry plugin |
TIP
Use GramIO's auto-retry plugin to handle 429 errors automatically.
Tips & Gotchas
- For Telegram Stars (
currency: "XTR"), omitprovider_token. Pass an empty string""or simply omit it. Stars payments don't go through an external provider. XTRinvoices must have exactly one price item. Thepricesarray must contain a singleLabeledPrice. Multiple items cause a400error.- Prices are in the smallest currency unit. For USD,
amount: 100= $1.00 (cents). For XTR (Stars),amount: 50= 50 Stars. Check theexpfield in currencies.json. - You MUST answer
pre_checkout_querywithin 10 seconds. Failure to callanswerPreCheckoutQueryin time causes the payment to fail on the user's side — always handle this event promptly. - The
payloadis your order identifier — store it. Usepayloadto link the payment to your internal order. It's returned in bothpre_checkout_queryandsuccessful_paymentevents. need_name,need_email, etc. are silently ignored for XTR payments. These data-collection flags only apply to fiat currency payments with a provider token.- If
start_parameteris empty, forwarded invoices remain payable. Setting astart_parameterconverts the Pay button in forwarded copies to a deep link, preventing others from paying the same forwarded invoice.
See Also
- answerPreCheckoutQuery — confirm or reject a payment before charging
- LabeledPrice — price item type used in
prices - Invoice — the Invoice type in received messages
- SuccessfulPayment — payment confirmation message type
- Keyboards overview — how to build inline keyboards
- auto-retry plugin — automatic
429handling