# Add pay-per-view to a Next.js app

A server route creates a drop and returns its link, you send the buyer there to pay on the 2fans checkout, and a signed webhook tells you when they’ve paid. Keep the key on the server.

## 1. Create the link

Keep `DROPFANS_API_KEY` in your server environment. The items must already be in the creator's vault.

```ts
// app/api/drop/route.ts
export async function POST(req: Request) {
  const { name, price, vaultItemIds } = await req.json();
  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, price, vaultItemIds }),
  });
  if (!res.ok) return Response.json(await res.json(), { status: res.status });
  const { productId, buyUrl } = await res.json();
  return Response.json({ productId, buyUrl });
}
```

Send the buyer to `buyUrl`. Add `?m=<your reference>` to tie the sale to your own chat or user.

## 2. Verify the webhook

Webhooks need an [approved app](https://2fans.dev/docs/build/connect-creators.md). Read the raw body before parsing it.

```ts
// app/api/webhooks/2fans/route.ts
import crypto from 'node:crypto';

function verify(rawBody: string, header: string, secret: string) {
  const parts = Object.fromEntries(header.split(',').map((p) => p.split('=')));
  const t = Number(parts.t);
  if (!t || Math.abs(Date.now() / 1000 - t) > 300) return false;
  const expected = crypto.createHmac('sha256', secret).update(t + '.' + rawBody).digest('hex');
  const a = Buffer.from(expected);
  const b = Buffer.from(parts.v1 ?? '');
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

export async function POST(req: Request) {
  const raw = await req.text();
  const signature = req.headers.get('DropFans-Signature') ?? '';
  if (!verify(raw, signature, process.env.TWOFANS_WEBHOOK_SECRET!)) {
    return new Response('Bad signature', { status: 400 });
  }
  const event = JSON.parse(raw);
  if (event.type === 'drop.paid') {
    // Queue the work and dedupe on event.data.orderId.
  }
  return new Response('ok');
}
```

Answer within 5 seconds and do the work afterwards. Without an approved app, poll `POST /drops/check-status` instead.

## 3. Let your agent do it

Give your coding agent [Let your AI set it up](https://2fans.dev/docs/ai/agent-setup.md). It opens each page, you handle the ID check, and it writes the rest.

## Key facts

| | |
| --- | --- |
| **How it works** | Your CRM creates a link and sends it in the chat. The buyer pays on the 2fans checkout and unlocks content hosted on 2fans. |
| **Fee** | 15% of each sale; the creator keeps 85%. No monthly, setup or listing fee. |
| **App commission** | Approved apps earn 5% of every sale made through their links, for life. It’s paid out of our fee, never taken from the creator. |
| **Payouts** | Earnings land in the creator’s 2fans balance and are released 14 days after each sale. From $20, they can request a payout as crypto, a bank transfer or to a card through MassPay, or to Paxum. All of it is included with the API. |
| **What you can sell** | Pay-per-view drops, weekly or monthly subscriptions, and tips, each with its own link you can send in Telegram or any chat. |
| **Prices** | Free, or $5 to $750 per drop, in USD. A drop holds up to 10 items. |
| **Content** | Photos, video, audio, PDFs and other digital media, including 18+ content and declared AI-generated content. |
| **Creator checks** | Creators verify with an ID document, face match and liveness check. Uploads need an approved check. |
| **App access** | For software other people’s creators connect to. A person reviews each application, usually within a few business days. 300 requests a minute and 50,000 a day, per key. |

## Create a pay-per-view link

```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": "Beach set", "price": 15, "vaultItemIds": ["VAULT_ITEM_A", "VAULT_ITEM_B"] }'
```

Response:

```json
{ "productId": "…", "buyUrl": "https://www.dropfans.io/buy/…", "mediaCount": 2 }
```

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

## Links

- [Apply for app access](https://2fans.io/developers/apply)
- [API docs](https://2fans.io/developers)
- [Docs index for LLMs](https://2fans.io/developers/llms.txt)
- [OpenAPI spec](https://2fans.io/developers/openapi.json)

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