2025, Nov 04 05:00
How to resolve Telethon RPCError 400: PAYMENT_REQUIRED when sending Telegram Stars gifts (invoice-based flow)
Facing RPCError 400: PAYMENT_REQUIRED in Telethon when using TransferStarGiftRequest? Use invoice flow with InputInvoiceStarGiftTransfer & SendStarsFormRequest
Sending a Telegram Stars gift with Telethon can unexpectedly fail with RPCError 400: PAYMENT_REQUIRED, even when the account has enough stars and the item is not under a trade ban. If a previously working function started returning this error, the underlying flow you call is the key detail to review.
Reproducing the issue
The function below attempts to transfer a saved Stars gift directly via TransferStarGiftRequest. The recipient is obtained through get_input_entity, and the gift payload contains its msg_id.
async def push_gift_async(tg, recipient_input, present_data):
try:
result = await tg(TransferStarGiftRequest(
stargift=InputSavedStarGiftUser(msg_id=present_data['msg_id']),
to_id=recipient_input
))
return True
except Exception as exc:
err_text = str(exc)
print(f"Gift sending failed: {err_text}")
return False, err_text
# elsewhere
# recipient_input = await tg.get_input_entity(user_id)
# present_data example: {"msg_id": 904312, "title": "Desk Calendar", "slug": "DeskCalendar-29932"}
In this setup, the call can throw the following error:
RPCError 400: PAYMENT_REQUIRED (caused by TransferStarGiftRequest)
What is actually going on
The transfer must follow the invoice-driven payment flow. Instead of invoking TransferStarGiftRequest directly, the server expects you to create an invoice for the transfer, retrieve a payment form, and then submit it. Without that sequence, you can run into PAYMENT_REQUIRED.
Fix: use the invoice and payment form flow
The working approach is to create an InputInvoiceStarGiftTransfer, fetch the form with GetPaymentFormRequest, and confirm it via SendStarsFormRequest. The msg_id still identifies the saved gift, and the recipient is provided as an InputUser with id and access_hash.
from telethon.tl.functions.payments import GetPaymentFormRequest, SendStarsFormRequest
from telethon.tl.types import InputUser, InputInvoiceStarGiftTransfer, InputSavedStarGiftUser
async def transfer_gift_via_invoice(tg, recipient_input, present_data):
bill_payload = InputInvoiceStarGiftTransfer(
stargift=InputSavedStarGiftUser(msg_id=present_data['msg_id']),
to_id=InputUser(
user_id=recipient_input.user_id,
access_hash=recipient_input.access_hash
)
)
pay_form = await tg(GetPaymentFormRequest(invoice=bill_payload))
await tg(SendStarsFormRequest(
form_id=pay_form.form_id,
invoice=bill_payload
))
return True
This sequence matches the expected payments flow for transferring a saved Stars gift.
Why this matters
Working with Telegram Stars means respecting the payments workflow the API enforces. Calling a low-level transfer without the invoice can lead to PAYMENT_REQUIRED, even if the balance looks fine. Using the invoice and form ensures the transfer aligns with what the server validates during the payment step.
Takeaways
If you hit RPCError 400: PAYMENT_REQUIRED on TransferStarGiftRequest, switch to the invoice path. Build InputInvoiceStarGiftTransfer for the selected saved gift’s msg_id and the exact recipient, fetch the form with GetPaymentFormRequest, then finalize via SendStarsFormRequest. This minimal change keeps your gift transfers stable and consistent with the current API flow.