No Fianto fee during the beta. Any fee comes with at least 30 days notice. See pricing
Use cases10 min read

Run a paid Telegram group or channel on USDC subscriptions.

A paid Telegram group on USDC works best with a join-request link: anyone can ask to join, your bot sends people who have not paid a Fianto checkout link, and it approves the request once Fianto's subscription.created webhook confirms the payment. When subscription.ended arrives, one Bot API call removes the member. Fianto collects the USDC into your wallet every 30 or 365 days; the Telegram side is a small bot you run.

On this page
What are the ways to charge for a Telegram group?Why use a join-request link instead of one invite link per member?How do you build the paid group, step by step?Which Fianto events approve and remove Telegram members?What happens when a member stops paying?Can you sell one-time Telegram access with no bot?What does a paid Telegram group cost, and which groups need approval?SourcesQuestions
TL;DR
  • Post one invite link with creates_join_request on. The bot approves a request only for a Telegram user ID with an active Fianto subscription, so a forwarded link lets nobody in who has not paid.
  • The bot puts the Telegram user ID in the checkout's customer_reference; Fianto returns it on every subscription event, and unbanChatMember on subscription.ended removes the member.
  • For one-time access, single-use invite links can be sold from a Fianto shop as delivery codes, with no bot at all. A trading-signals group needs Fianto's written approval before the first collection, and approval is not automatic.

What are the ways to charge for a Telegram group?

There are three workable setups, and they differ in who pays how and whether access ever stops. Fianto has no Telegram bot or integration, so two of the three need a bot you run. As of 15 October 2026:

SetupPaid inRenewsA bot of yoursWorks for
Telegram's own subscription invite linkTelegram StarsEvery 30 daysNot needed: made in channel settingsChannels
Single-use invite links sold as Fianto shop delivery codesUSDCNo: one-time accessNot neededGroups and channels
Join-request link, your bot and Fianto webhooksUSDCEvery 30 or 365 daysApproves and removes membersSupergroups and channels

Telegram's built-in option is honest competition. The Bot API method createChatSubscriptionInviteLink makes "a subscription invite link for a channel chat", priced in Stars, and the period "must always be 2592000 (30 days)", at 1 to 10,000 Stars. Telegram's own announcement of 14 August 2024 puts it in the app, under Channel Settings > Channel Type > Manage Invite Links, with "Require Monthly Fee" turned on. If your readers already buy Stars and you run a channel, it is the shortest path. It is not USDC, it has no yearly price, and it is for channels, so a discussion group or a yearly plan billed in USDC needs the third setup.

Why use a join-request link instead of one invite link per member?

A join-request link ties access to a Telegram account instead of to whoever holds the link. With creates_join_request set, Telegram does not let people in directly; it sends your bot a ChatJoinRequest with the user who asked, and the bot decides. A link with member_limit: 1 (the range is 1 to 99,999) is the other common pattern, but whoever opens it first gets in, paid or not. Telegram does not let you combine the two: "If True, member_limit can't be specified."

The join request also solves the hardest part of the build, knowing who paid. The request carries from.id, the Telegram user ID, and a user_chat_id that lets the bot message that person even if they never opened a chat with it. Telegram says "The bot can use this identifier for 5 minutes to send messages until the join request is processed, assuming no other administrator contacted the user." So the bot should reply with the checkout link straight away.

How do you build the paid group, step by step?

  1. Turn the group into a supergroup, or use a channel, and add your bot as an administrator with the rights to invite users and to ban users. Join requests reach the bot only with the can_invite_users right.
  2. Have the bot call createChatInviteLink with creates_join_request set to true and a name of up to 32 characters, and post that link wherever you sell.
  3. In Fianto, create a product with a monthly or yearly price. Payments start once your merchant account is approved.
  4. When a join request arrives, approve it at once if that user ID already has an active subscription in your records. Otherwise create a Fianto checkout session for them and message the link through user_chat_id.
  5. On subscription.created, approve the request. On subscription.ended, remove the member.
bot/join-requests.ts
import { Fianto } from '@fianto/sdk';
import { db } from './db'; // your own store

const fianto = new Fianto(); // reads FIANTO_APP_ID and FIANTO_APP_SECRET
const TG = `https://api.telegram.org/bot${process.env.TELEGRAM_BOT_TOKEN}`;

export async function tg(method: string, body: object) {
  const res = await fetch(`${TG}/${method}`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(body),
  });
  return res.json();
}

// Called for each chat_join_request update.
export async function onJoinRequest(req: { chat: { id: number }; from: { id: number }; user_chat_id: number }) {
  const userId = String(req.from.id);
  if (await db.isActive(userId)) {
    return tg('approveChatJoinRequest', { chat_id: req.chat.id, user_id: req.from.id });
  }
  const orderId = `tg_${userId}_${Date.now()}`;
  const session = await fianto.checkoutSessions.create(
    {
      mode: 'subscription',
      order_id: orderId,
      price_id: 'fian_price_...',
      customer_reference: userId,
      metadata: { chat_id: String(req.chat.id) },
      success_url: 'https://example.com/paid',
      cancel_url: 'https://example.com/join',
    },
    { idempotencyKey: orderId },
  );
  return tg('sendMessage', {
    chat_id: req.user_chat_id,
    text: `Subscribe in USDC here, then come back to Telegram: ${session.url}`,
  });
}

The checkout session is open for 30 minutes by default, and its URL comes back only when the session is created, so send it right away. After a member subscribes, Fianto's checkout page offers links to the customer portal rather than opening success_url, which is why the message tells them to come back to Telegram.

Members approve once in their wallet and renew every 30 or 365 days. You pay nothing.

Grant access from webhooks

Which Fianto events approve and remove Telegram members?

Approve the join request on subscription.created and remove the member on subscription.ended; both events carry the user ID back as data.customer.reference and your chat ID in data.metadata. A subscription creates no order, so order.paid never fires for it.

bot/fianto-webhooks.ts
import { webhooks } from '@fianto/express';
import { tg } from './join-requests';
import { db } from './db';

export const fiantoRoute = webhooks({
  secret: process.env.FIANTO_WEBHOOK_SECRET,
  onSubscriptionCreated: async (event) => {
    const userId = event.data.customer.reference;
    const chatId = event.data.metadata.chat_id;
    if (userId === null || (await db.seen(event.id))) return;
    await db.activate(event.data.id, userId, event.data.current_period_end);
    // Fails harmlessly if the request is no longer pending: the member taps the link again and is approved.
    await tg('approveChatJoinRequest', { chat_id: chatId, user_id: Number(userId) });
    await db.markSeen(event.id);
  },
  onSubscriptionEnded: async (event) => {
    const userId = event.data.customer.reference;
    const chatId = event.data.metadata.chat_id;
    if (userId === null || (await db.seen(event.id))) return;
    await db.deactivate(event.data.id);
    // Delivery is unordered: a newer subscription from the same person may already be paying.
    if ((await db.isActive(userId)) === false) {
      // Without only_if_banned, this removes a current member and leaves them free to rejoin later.
      const res = await tg('unbanChatMember', { chat_id: chatId, user_id: Number(userId) });
      // Rate limited or a Telegram outage: throw, the route answers 500 and Fianto retries.
      if (res.ok === false && (res.error_code === 429 || res.error_code >= 500)) throw new Error(res.description);
    }
    await db.markSeen(event.id);
  },
});

The removal is a quirk worth knowing. unbanChatMember on someone who was never banned removes them. Telegram's description says: "By default, this method guarantees that after the call the user is not a member of the chat, but will be able to join it." banChatMember is the stronger tool, for someone who should not come back by any link. Mount the Fianto route before any body parser, record each event id once, and expect events at least once and in no fixed order, which is why the ending checks for a newer subscription first; verifying webhook signatures has the details.

What happens when a member stops paying?

A failed renewal makes the subscription past due, not ended. Fianto tries four times in all: at the period boundary, then about one hour, six hours and a day after it. Your bot gets subscription.past_due on the first counted failure and subscription.payment_failed on each one; whether you remove the member then or wait is up to you. After the fourth counted failure the subscription ends with PAYMENT_FAILED, and the period is never charged later.

  • A member cancels in the customer portal at fianto.xyz/my with one signature. The subscription runs to the end of the paid period, so they stay until subscription.ended.
  • Removing the approval inside the wallet is not a cancel. The next renewal fails, the subscription goes past due, and it ends after the fourth failed attempt.
  • Fianto sends subscribers no renewal reminders. If your members expect a note before a yearly renewal, have the bot send it from current_period_end.
  • There is no refund button. A refund is USDC you send back from your own wallet.

Can you sell one-time Telegram access with no bot?

Yes, if access never has to end: create single-use invite links in Telegram and sell them from a Fianto shop as delivery codes. Each paid buyer gets one code, shown on the receipt and in the buyer email, and the product shows as sold out when the list runs out. A code can be up to 200 characters, which fits a t.me link, and a product holds up to 5,000 unused codes. Create each link with a member limit of 1 and no expiry date, since a code may wait weeks before someone buys it. Telegram counts that limit as members who joined through the link at the same time, so if the buyer leaves, the link admits one more person.

Two cautions. A shop receipt page is not private, so anyone with its link sees the code; a single-use link that the buyer has already used is worth nothing to them. And nothing removes the buyer later, so use this only for lifetime access. The shop itself is covered in the no-code USDC shop post. Delivery codes in depth are in selling licence keys for USDC, and the same codes can enrol students, as in selling an online course for USDC.

What does a paid Telegram group cost, and which groups need approval?

You pay nothing, and the price you set lands in your wallet. There is no Fianto fee during the beta; after it, the member pays about 0.1 to 0.5% on top. The member pays a flat 0.01 USDC network fee with each charge today, the first one included, on plans created since 6 October 2026, the Solana fee of the transaction they sign (under $0.01), and account rent: about 0.00144 SOL as of 15 October 2026 for the subscription account, returned when they reclaim it after the subscription ends, and about 0.00119 SOL once for wallet setup on a wallet's first subscription, not returned. The checkout shows the exact live amounts before anything is signed.

Many paid Telegram groups sell trading signals. Fianto's acceptable-use policy lists "Financial or investment education and signals" among the businesses allowed only after Fianto has reviewed them and confirmed in writing, and says Fianto usually asks what you promise customers and what your refund terms are. Approval can come with conditions or not at all, and the same policy prohibits investment schemes and unlicensed financial services outright. Raise it during review, before you post the link. This is not legal advice; the rules that apply to selling signals depend on where you and your members are.

Event names and payloads are in the Fianto webhook docs, and the same pattern for Discord roles is in paid Discord roles with USDC.

Sources
Questions

Questions about paid Telegram groups with USDC.

Does Fianto have a Telegram bot?

No. Fianto bills members in USDC and sends signed webhook events. A bot you run approves join requests and removes members from those events.

Can someone forward my invite link to a friend who has not paid?

Not with a join-request link: Telegram asks your bot about every request, and the bot approves only user IDs with an active subscription.

How do I remove a member whose subscription ended?

Call unbanChatMember without only_if_banned. Telegram removes a current member and lets them rejoin later, for example after they subscribe again.

Can I charge yearly for a Telegram group in USDC?

Yes. A Fianto price can renew every 365 days as well as every 30 days. Telegram's own Stars subscription links are fixed at 30 days.

Do I need approval to sell a trading signals group?

Yes, before the first collection. Fianto's acceptable-use policy lists financial or investment education and signals among businesses allowed only after Fianto reviews them and confirms in writing, and that approval is not guaranteed.

Read next.

Use casesDevelopers10 min read

Paid Discord roles with USDC: a bot, a checkout and two webhook events

To sell a Discord role for USDC, run a bot that holds the Manage Roles permission, let a /join command open a Fianto subscription checkout with the member's Discord user ID as the customer reference, and add or remove the role when Fianto's subscription.created and subscription.ended webhooks arrive. Fianto has no Discord integration of its own: it collects the USDC into your wallet and tells your server what happened, and your bot does the Discord part.

Use casesProduct8 min read

Sell licence keys and game keys for USDC with shop delivery codes

Fianto's shop sells licence keys and game keys through delivery codes: you paste your keys into a product, each buyer who pays in USDC gets exactly one, on the receipt and in the buyer email, and the product shows as sold out when none are left. A product holds up to 5,000 unused keys, a key is reserved while its buyer pays so two buyers never get the same one, and you are emailed when five or fewer remain.

Product7 min read

Zapier and Make automations for USDC shop sales

A Fianto shop can send its sales to Zapier or Make with no code: paste a Zapier "Catch Hook" URL or a Make "Custom webhook" URL into Connect an app in your shop settings. Fianto then posts two events as flat JSON, purchase.paid when someone pays and subscription.ended when a subscription bought in the shop ends. Only shop sales are sent: payment link and API sales, renewals and failed renewals are not.

One link, paying members only.

Create the join-request link, then let subscription events open and close the door.

New posts are announced on X and Discord.