# Sell on Telegram and in chats

Create one drop, give each fan a personal pay-per-view link and match every sale back to the right chat.

One drop can be sold to any number of fans. Give each one a link with a reference of your choice, such as your id for the conversation. The paid order comes back carrying that reference.

## 1. Create the drop once

One drop has one price, so create one drop per price. Keep the `productId`.

curl:

```bash
curl -X POST "https://www.dropfans.io/api/external/v1/drops" \
  -H "Authorization: Bearer $DROPFANS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
      "name": "Friday set",
      "price": 25,
      "vaultItemIds": [
        "VAULT_ITEM_A",
        "VAULT_ITEM_B"
      ]
    }'
```

Node:

```javascript
const res = await fetch('https://www.dropfans.io/api/external/v1/drops', {
  method: 'POST',
  headers: {
    Authorization: 'Bearer ' + process.env.DROPFANS_API_KEY,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "name": "Friday set",
    "price": 25,
    "vaultItemIds": [
      "VAULT_ITEM_A",
      "VAULT_ITEM_B"
    ]
  }),
});
console.log(await res.json());
```

Python:

```python
import os, requests

res = requests.post(
    "https://www.dropfans.io/api/external/v1/drops",
    headers={"Authorization": f"Bearer {os.environ['DROPFANS_API_KEY']}"},
    json={
      "name": "Friday set",
      "price": 25,
      "vaultItemIds": [
        "VAULT_ITEM_A",
        "VAULT_ITEM_B"
      ]
    },
)
print(res.json())
```

## 2. Make one link per fan

A reference is 1 to 32 characters of `A-Z a-z 0-9 _ -`. `GET /links` returns the right templates for each creator. The fan pays on the web checkout. Telegram requires Stars for digital goods sold inside bots and Mini Apps, so this is the link to send.

Link:

```text
https://www.dropfans.io/buy/{productId}?m={externalRef}
```

## 3. Match the payment

Approved apps get a `drop.paid` webhook, where `data.externalRef` says which fan paid. Use `data.orderId` as your idempotency key. Personal keys can poll `GET /orders?externalRef=…`.

A fan can edit the link, so the reference is not proof of identity. For extra safety, compare `buyer.telegramUserId` with the chat you expect.

Node:

```javascript
// drop.paid handler (after verifying the signature)
const { orderId, externalRef, productId, buyer } = event.data;
if (await alreadyDelivered(orderId)) return;
const chat = await findConversationByRef(externalRef);
await deliverMedia(chat, productId);
await markDelivered(orderId);
```

## 4. Handle refunds

Refunds and chargebacks arrive as `drop.refunded` with the same `orderId` and `externalRef`, plus a `reason` of `refund` or `chargeback`.

Event:

```json
{
  "type": "drop.refunded",
  "data": {
    "orderId": "...",
    "externalRef": "tg_48213377",
    "refundedAt": "2026-10-17T16:20:00.000Z",
    "reason": "chargeback"
  }
}
```

## Subscriptions

Subscribe links work the same way. See [Paid subscriptions for Telegram](https://2fans.dev/telegram-subscriptions.md).

## Selling inside your own bot

Don't wire card payments into a bot invoice or a Mini App. If your own bot or Mini App charges for digital goods itself, Telegram requires Stars. Developers receive about $0.013 per Star, after a hold of up to 21 days. See Telegram's [Stars docs](https://core.telegram.org/bots/payments-stars) and [developer terms](https://telegram.org/tos/bot-developers).

## Next

- [Webhooks](https://2fans.dev/docs/build/webhooks.md): Set one signed endpoint for your app and react to sales, refunds, subscriptions, payouts and ID-check results.
- [Refunds and chargebacks](https://2fans.dev/docs/handled/chargebacks.md): Who deals with disputes, what a reversal does to access and earnings, and the webhook events your app gets.

2fans was called Dropfans until October 2026, so the API host is still www.dropfans.io and keys still start with dpfn_.

Last updated 2026-11-02. All pages: https://2fans.dev/llms.txt.