Monetize Your Channel
Your Way

Connect Stripe, PayPal, Patreon, or any payment provider. We handle the access codes and email delivery. You keep 100% of the revenue.

Supported Payment Providers

Pick the payment provider you already use. We connect to it, generate access codes automatically, and email your customers their personal link.

Direct Payment — we host a checkout page for you

Stripe

Accept cards, Apple Pay, Google Pay. We create Stripe Checkout sessions with your API key.

Hosted checkout

Revolut Business

Accept payments via Revolut checkout. Fast setup, supports multiple currencies.

Hosted checkout

PayPal

Accept PayPal payments. Buyers pay with their PayPal account or credit card.

Hosted checkout

Creator Platforms — they handle payment, we handle access

Patreon

Patrons automatically get access when they pledge at your minimum tier.

Webhook

Gumroad

Sell access as a Gumroad product. Buyers get their link after purchase.

Webhook

LemonSqueezy

Sell access through your Lemon Squeezy store. Handles tax and payments.

Webhook

Ko-fi

Supporters who buy you a "coffee" or join your membership get channel access.

Webhook

Buy Me a Coffee

Supporters and members automatically receive access to your channel.

Webhook

You keep 100% of the revenue. We never touch your money. Payments go directly to your account on the provider you choose. We only generate the access codes and email the link.

Connect Your Provider

Sign in, pick your payment provider, enter your API credentials, and we give you either a payment link (for Stripe/Revolut/PayPal) or a webhook URL (for Patreon/Gumroad/etc.).

1

Sign in below

Use the same Apple account you use in the My TV Channel app.

2

Pick provider & enter keys

Select your payment provider and enter your API key or webhook secret. Set price and duration.

3

Share your link

Get a payment link to share with your audience, or paste the webhook URL into your provider's settings.

Connect your payment provider

Sign in with the same Apple account you use in the app.

Sign in with Apple

Where to find your Stripe API key: Go to Stripe Dashboard → Developers → API keys → copy your Secret key (starts with sk_live_).

What your customers will receive

After a successful payment, your customer receives an email with their personal access link:

Your access is ready

Thanks for your purchase! You now have access to Your Channel Name.

Watch Now

This link is personal. It gives you 30 days of access.

For Developers

How It Works

You handle the payment. We handle the access. When a customer pays you (through your website, Stripe, Patreon, or any other method), your server calls our API to generate a unique access code. You give that code to your customer as a link.

1

Customer pays you

On your website, Patreon, or any payment system you choose.

2

Your server calls our API

Generate a subscriber code with the expiration and limits you want.

3

Customer gets a link

Like https://your-channel.localtvbroadcast.com/?subscriber=ABC2DEF

4

Customer watches

Opens in the app (with access granted) or plays directly in the web browser.

You keep 100% of the revenue. We don't process payments or take a commission. Subscriber codes simply grant access to your channel within My TV Channel.

Authentication

All publisher API calls require a JWT token. Obtain one by signing in with your My TV Channel account:

# Sign in to get your JWT token
curl -X POST https://localtvbroadcast.com/api/mytvchannel/auth/login.php \
  -H "Content-Type: application/json" \
  -d '{
    "email": "you@example.com",
    "password": "your-password"
  }'

The response includes a token field. Use it in all subsequent requests:

Authorization: Bearer YOUR_JWT_TOKEN

Keep your token secure. Store it as a server-side environment variable. Never expose it in client-side code or public repositories.

Base URL

All API endpoints use the following base URL:

https://localtvbroadcast.com/api/mytvchannel/subscriber-codes/

Generate a Code

POST /subscriber-codes/generate.php

Create a new subscriber access code for your channel. Requires authentication and channel ownership.

Request Body

FieldTypeRequiredDescription
channel_idintegerrequiredYour channel's ID
expires_atstringrequiredISO 8601 date when the code stops accepting new redemptions
labelstringoptionalInternal label (e.g., "VIP Monthly Pass")
pricenumberoptionalYour price (metadata only, we don't process payment)
currencystringoptionalISO 4217 currency code (default: USD)
max_redemptionsintegeroptionalMax uses. null = unlimited
access_duration_daysintegeroptionalDays of access per redemption. null = access until expires_at

Example

curl -X POST https://localtvbroadcast.com/api/mytvchannel/subscriber-codes/generate.php \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "channel_id": 42,
    "expires_at": "2026-12-31T23:59:59Z",
    "label": "Monthly Pass - March",
    "price": 9.99,
    "currency": "USD",
    "max_redemptions": 1,
    "access_duration_days": 30
  }'

Response

{
  "success": true,
  "message": "Subscriber code generated",
  "data": {
    "id": 1,
    "code": "A3F7K2N",
    "url": "https://your-channel.localtvbroadcast.com/?subscriber=A3F7K2N",
    "channel_id": 42,
    "label": "Monthly Pass - March",
    "price": 9.99,
    "currency": "USD",
    "max_redemptions": 1,
    "expires_at": "2026-12-31T23:59:59+00:00",
    "access_duration_days": 30
  }
}

Single-use vs. multi-use codes: Set max_redemptions: 1 for a unique code per customer. Set it to null for a shared promo code that anyone can use.

Access Duration Explained

Two models are supported:

Scenarioaccess_duration_daysBehavior
Time-limited pass30Each viewer gets 30 days from the moment they redeem
Event passnullAll viewers have access until the code's expires_at date

List Codes

GET /subscriber-codes/list.php?channel_id={id}

Retrieve all subscriber codes for your channel, with redemption stats.

Example

curl https://localtvbroadcast.com/api/mytvchannel/subscriber-codes/list.php?channel_id=42 \
  -H "Authorization: Bearer YOUR_TOKEN"

Response

{
  "success": true,
  "data": {
    "codes": [
      {
        "id": 1,
        "code": "A3F7K2N",
        "url": "https://your-channel.localtvbroadcast.com/?subscriber=A3F7K2N",
        "label": "Monthly Pass - March",
        "price": 9.99,
        "currency": "USD",
        "max_redemptions": 1,
        "redemption_count": 0,
        "expires_at": "2026-12-31T23:59:59+00:00",
        "access_duration_days": 30,
        "is_active": true,
        "created_at": "2026-02-21 14:00:00"
      }
    ]
  }
}

Revoke a Code

POST /subscriber-codes/revoke.php

Deactivate a subscriber code. The code will no longer accept new redemptions. Existing access already granted remains valid until its own expiration.

Request Body

FieldTypeRequiredDescription
code_idintegerrequiredThe ID of the code to revoke (from generate or list response)

Example

curl -X POST https://localtvbroadcast.com/api/mytvchannel/subscriber-codes/revoke.php \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "code_id": 1 }'

Redeem a Code (App)

POST /subscriber-codes/redeem.php

Used by the My TV Channel app when a viewer opens a universal link. Requires user authentication. Idempotent — re-redeeming the same code returns the existing access.

Request Body

FieldTypeRequiredDescription
codestringrequiredThe 7-character subscriber code

Response

{
  "success": true,
  "message": "Access granted",
  "data": {
    "channel": {
      "id": 42,
      "handle": "your-channel",
      "name": "Your Channel",
      "description": "...",
      "thumbnail_url": "..."
    },
    "access_expires_at": "2026-04-21T14:00:00+00:00",
    "already_redeemed": false
  }
}

Note: This endpoint is called automatically by the app. You don't need to call it from your server.

Validate a Code (Web Player)

POST /subscriber-codes/validate.php

Used by the web player to validate a subscriber code before starting playback. No authentication required. Called automatically when a viewer opens a channel link with ?subscriber=.

Request Body

FieldTypeRequiredDescription
codestringrequiredThe 7-character subscriber code
channel_idintegerrequiredChannel ID to validate against

Note: This endpoint is called automatically by the web player. You don't need to call it from your server.

Stripe Integration

Generate a subscriber code automatically when a Stripe payment succeeds. Add this webhook handler to your server:

// Express.js webhook handler for Stripe
const express = require('express');
const stripe = require('stripe')('sk_...');

const MY_TV_API = 'https://localtvbroadcast.com/api/mytvchannel';
const MY_TV_TOKEN = process.env.MY_TV_TOKEN;  // Your JWT token
const CHANNEL_ID = 42;  // Your channel ID

app.post('/webhooks/stripe', express.raw({ type: 'application/json' }), async (req, res) => {
  const sig = req.headers['stripe-signature'];
  const event = stripe.webhooks.constructEvent(req.body, sig, 'whsec_...');

  if (event.type === 'checkout.session.completed') {
    const session = event.data.object;

    // Generate a subscriber code
    const resp = await fetch(`${MY_TV_API}/subscriber-codes/generate.php`, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${MY_TV_TOKEN}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        channel_id: CHANNEL_ID,
        expires_at: '2027-01-01T00:00:00Z',
        label: `Stripe ${session.id}`,
        price: session.amount_total / 100,
        currency: session.currency.toUpperCase(),
        max_redemptions: 1,
        access_duration_days: 30
      })
    });

    const { data } = await resp.json();

    // Send the link to your customer
    await sendEmail(session.customer_email, {
      subject: 'Your access link',
      body: `Watch here: ${data.url}`
    });
  }

  res.json({ received: true });
});
# Flask webhook handler for Stripe
import stripe, requests, os
from flask import Flask, request, jsonify

app = Flask(__name__)
stripe.api_key = 'sk_...'

MY_TV_API = 'https://localtvbroadcast.com/api/mytvchannel'
MY_TV_TOKEN = os.environ['MY_TV_TOKEN']
CHANNEL_ID = 42

@app.route('/webhooks/stripe', methods=['POST'])
def stripe_webhook():
    sig = request.headers['Stripe-Signature']
    event = stripe.Webhook.construct_event(request.data, sig, 'whsec_...')

    if event['type'] == 'checkout.session.completed':
        session = event['data']['object']

        # Generate a subscriber code
        resp = requests.post(
            f'{MY_TV_API}/subscriber-codes/generate.php',
            headers={
                'Authorization': f'Bearer {MY_TV_TOKEN}',
                'Content-Type': 'application/json'
            },
            json={
                'channel_id': CHANNEL_ID,
                'expires_at': '2027-01-01T00:00:00Z',
                'label': f'Stripe {session["id"]}',
                'price': session['amount_total'] / 100,
                'currency': session['currency'].upper(),
                'max_redemptions': 1,
                'access_duration_days': 30
            }
        )

        url = resp.json()['data']['url']
        send_email(session['customer_email'], f'Watch here: {url}')

    return jsonify(received=True)
// PHP webhook handler for Stripe
<?php
$payload = file_get_contents('php://input');
$sig = $_SERVER['HTTP_STRIPE_SIGNATURE'];
$event = \Stripe\Webhook::constructEvent($payload, $sig, 'whsec_...');

$myTvApi = 'https://localtvbroadcast.com/api/mytvchannel';
$myTvToken = getenv('MY_TV_TOKEN');
$channelId = 42;

if ($event->type === 'checkout.session.completed') {
    $session = $event->data->object;

    // Generate a subscriber code
    $ch = curl_init("{$myTvApi}/subscriber-codes/generate.php");
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER => [
            "Authorization: Bearer {$myTvToken}",
            'Content-Type: application/json'
        ],
        CURLOPT_POSTFIELDS => json_encode([
            'channel_id' => $channelId,
            'expires_at' => '2027-01-01T00:00:00Z',
            'label' => "Stripe {$session->id}",
            'price' => $session->amount_total / 100,
            'currency' => strtoupper($session->currency),
            'max_redemptions' => 1,
            'access_duration_days' => 30
        ])
    ]);

    $resp = json_decode(curl_exec($ch));
    curl_close($ch);

    // Send the link to your customer
    mail($session->customer_email, 'Your access link', "Watch here: {$resp->data->url}");
}

http_response_code(200);
echo json_encode(['received' => true]);

Patreon Integration (Self-Hosted)

If you prefer to run your own webhook handler instead of using our hosted integration, here's how to handle the members:pledge:create event on your server:

// Node.js - Patreon webhook handler
const crypto = require('crypto');

app.post('/webhooks/patreon', express.json(), async (req, res) => {
  // Verify Patreon signature
  const signature = req.headers['x-patreon-signature'];
  const hash = crypto
    .createHmac('md5', process.env.PATREON_WEBHOOK_SECRET)
    .update(JSON.stringify(req.body))
    .digest('hex');

  if (signature !== hash) return res.status(403).send('Invalid signature');

  const event = req.headers['x-patreon-event'];
  const { data, included } = req.body;

  if (event === 'members:pledge:create') {
    const patron = included.find(i => i.type === 'user');
    const email = patron?.attributes?.email;
    const amountCents = data.attributes.currently_entitled_amount_cents;

    // Only grant access for tiers >= $5
    if (amountCents >= 500) {
      const resp = await fetch(
        'https://localtvbroadcast.com/api/mytvchannel/subscriber-codes/generate.php',
        {
          method: 'POST',
          headers: {
            'Authorization': `Bearer ${process.env.MY_TV_TOKEN}`,
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({
            channel_id: 42,
            expires_at: '2027-01-01T00:00:00Z',
            label: `Patreon: ${email}`,
            max_redemptions: 1,
            access_duration_days: 30
          })
        }
      );

      const { data: codeData } = await resp.json();

      // Send via Patreon message or your own email service
      await sendEmail(email, {
        subject: 'Your TV Channel access is ready!',
        body: `Thanks for your support! Watch here: ${codeData.url}`
      });
    }
  }

  res.json({ received: true });
});

Generic Webhook / Custom Payment

For any payment system with webhooks (PayPal, Gumroad, LemonSqueezy, Ko-fi, Buy Me a Coffee, etc.), the pattern is the same:

1

Receive payment webhook

Verify the webhook signature per your provider's documentation.

2

Call generate endpoint

POST /subscriber-codes/generate.php with your JWT token.

3

Deliver the link

Send data.url to your customer via email, redirect, or in-app message.

Minimal cURL Example

# Generate a single-use code valid for 30 days of access
curl -X POST https://localtvbroadcast.com/api/mytvchannel/subscriber-codes/generate.php \
  -H "Authorization: Bearer $MY_TV_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "channel_id": 42,
    "expires_at": "2027-01-01T00:00:00Z",
    "max_redemptions": 1,
    "access_duration_days": 30
  }'

# Response includes the link to share:
# "url": "https://your-channel.localtvbroadcast.com/?subscriber=A3F7K2N"

Error Handling

HTTP StatusMeaning
200Success
400Missing or invalid fields (check message for details)
401Invalid or expired JWT token
403Channel not owned by authenticated user
404Invalid or expired subscriber code (redeem/validate)
500Server error (retry)