Recipe: a catalog site with free, subscription and paid access
How to put a creator's videos on your own site — free titles, members-only titles and pay-per-view titles — with what the platform does today. Every behaviour below is read from the running code; where something you would expect is missing, this page says so.
pk_ key represents the creator, not your viewers. You build the catalog with the key on your server, and you let the embeddable player run the paywall: it sells a video to a guest by email, sends subscribers through a DCAST sign-in, and accepts author access keys. The Partner API checkout endpoints buy as the key owner, so they cannot sell to many viewers over one key, and there is no Partner API method today that grants a specific viewer access to a paid video or tier.1. The access model of a video
Four fields on every video decide who can play it. You set them with PATCH /videos/:id and read them from GET /videos and GET /videos/:id.
| Field | Values | Effect |
|---|---|---|
visibility | PUBLIC, LINK_ONLY, PRIVATE, SUBSCRIBERS, TIER_EXCLUSIVE, REGISTERED | Who may watch at all, and whether the video is listed on the creator's channel. Full table: Videos → Visibility. |
isPaid + price | boolean, number | Pay-per-view. The viewer needs a completed purchase of this video (or an author access key for it). The video row has no currency column. |
requiredTierId | tier id or null | Membership gate. The viewer needs to be signed in to DCAST with an active subscription to a tier of this creator priced at least as high as the required tier. |
The checks stack: visibility is applied first, then the membership gate, then the purchase gate. A PUBLIC video with isPaid: true is listed publicly but still needs a purchase to play. When a video is both paid and tier-gated, a completed purchase (or an access-key grant) by a signed-in viewer opens it without the subscription.
# Free title
curl -s -X PATCH "https://api.dcast.pro/api/v1/videos/VIDEO_ID" -H "Authorization: Bearer pk_YOUR_KEY_HERE" \
-H "Content-Type: application/json" -d '{"visibility":"PUBLIC","isPaid":false,"requiredTierId":null}'
# Pay-per-view title, listed on the channel
curl -s -X PATCH "https://api.dcast.pro/api/v1/videos/VIDEO_ID" -H "Authorization: Bearer pk_YOUR_KEY_HERE" \
-H "Content-Type: application/json" -d '{"visibility":"PUBLIC","isPaid":true,"price":4.99}'
# Members-only title (tier id from GET /tiers)
curl -s -X PATCH "https://api.dcast.pro/api/v1/videos/VIDEO_ID" -H "Authorization: Bearer pk_YOUR_KEY_HERE" \
-H "Content-Type: application/json" -d '{"visibility":"TIER_EXCLUSIVE","requiredTierId":"TIER_ID"}'Tiers are created with POST /tiers (needs a FULL_ACCESS key or finance:write) — see Monetization.
2. Build the catalog
Call these from your server. The pk_ key is a secret: never ship it to the browser.
| Call | Auth | What you get |
|---|---|---|
GET /videos?page=&limit=&visibility= | key | All of the key owner's videos, any visibility, with isPaid, price, requiredTierId, status, thumbnails and playback (hls + embed, null until the HLS output exists). The visibility filter takes the exact stored names. |
GET /videos/:id | key (owner only) | The full row plus the playback block with requiresToken and accessTokenUrl (section 3). |
GET /channel/:username/videos | public | The channel's PUBLIC, fully processed videos. No paid fields on these rows. Sent with the owner's key, it returns all of the owner's videos. |
GET /channel/:username/playlists, GET /playlists/:id | public | Public playlists and their items. GET /me/playlists (key) lists all of yours. There is no GET /playlists list route (405). |
GET /videos/:id/related | public | Other PUBLIC videos of the same creator. The anchor must be PUBLIC. |
GET /tiers, GET /channel/:username | key / public | Your tiers (id, name, prices) / the public tier roster of a channel. |
GET /search, /trending, /feed | — | Withdrawn: 410 CATALOG_DISABLED. DCAST publishes no cross-creator catalog. See Discovery. |
# Your whole library, 50 per page (server-side)
curl -s -H "Authorization: Bearer pk_YOUR_KEY_HERE" "https://api.dcast.pro/api/v1/videos?page=1&limit=50"
# One title with its playback block
curl -s -H "Authorization: Bearer pk_YOUR_KEY_HERE" "https://api.dcast.pro/api/v1/videos/VIDEO_ID"
# → data.playback = { playable, hls, embed, playUrl, accessTokenUrl, thumbnail, requiresToken }Keep your catalog fresh with webhooks (video.processing.completed, video.processing.failed) instead of polling — see Webhooks.
3. Playback
Option A — the embeddable player (recommended for paid content)
Put playback.embed (https://dcast.tv/embed/VIDEO_ID) in an <iframe>. Any domain may embed it unless the creator has set an embed allowlist (Account). The player asks the platform for a pass itself and, when it is refused, shows a paywall that depends on the reason:
| Video | What the viewer sees in the player |
|---|---|
Free (PUBLIC / LINK_ONLY, not paid, no tier) | Plays immediately. |
Paid (isPaid) | A purchase dialog. The viewer pays with an email address and does not need a DCAST account (the purchase may create one for that email). After payment the player keeps the purchase code in that browser and replays it on later visits. |
Tier-gated (requiredTierId / TIER_EXCLUSIVE) or SUBSCRIBERS | A subscribe action. Without a DCAST session the player sends the viewer to the DCAST sign-in page; signed in, it opens the subscription payment dialog. |
| Paywalled video + author access key | A key field on the paywall. A valid video key for this video unlocks playback without payment, and the player remembers it in that browser. |
REGISTERED | The embedded player does not show the registration form for videos today. |
LINK_ONLY with a passphrase | Known limitation: the embedded player currently shows the private state without a key field. |
PRIVATE | Does not play (owner and platform admins only). |
Payment requests made inside the player come from the DCAST player origin, so the integration-domain requirement described in Monetization → Domain verification applies to your own /checkout/* calls, not to the iframe.
Option B — your own player
Read playback.requiresToken on GET /videos/:id:
false—playback.hlsplays as-is in any HLS player.true—playback.hlsanswers 403 until you obtain a pass fromplayback.accessTokenUrl.
accessTokenUrl is GET https://api.dcast.pro/api/videos/VIDEO_ID/access-token. It is not part of /api/v1 and does not read your pk_ key: it decides for the viewer. It admits a request when one of these holds:
| Viewer presents | Opens |
|---|---|
| Nothing | Free videos only. |
Authorization: Bearer <DCAST sign-in token> | Whatever that DCAST account is entitled to: its purchases, its subscriptions and tiers, its own videos. |
?accessCode=<code> | A completed purchase's access code (guest purchases), or an author access key scoped to this video. |
?accessKey=<passphrase> | A LINK_ONLY video protected by a passphrase. |
?regToken=<token> | A REGISTERED video, with a registration pass for it. |
curl -s "https://api.dcast.pro/api/videos/VIDEO_ID/access-token?accessCode=A1B2C3D4E5F60718"
# 200
{ "success": true,
"masterUrl": "https://<delivery-host>/video/VIDEO_ID/master.m3u8?token=...",
"expiresIn": 300 }
# 401 (sign-in needed) or 403 (refused)
{ "error": "Access denied", "reason": "payment_required", "message": "...",
"paywall": { "requiresPurchase": true, "requiresSubscription": false, "requiresTier": false,
"price": 4.99, "tierId": null, "tierName": null, "creatorId": "..." } }Play masterUrl. The pass for the master playlist is valid for 300 seconds; segment and key passes for 1800 seconds. A 503 with DELIVERY_WORKER_UNAVAILABLE means no delivery host could be chosen at that moment — retry.
4. Selling
The Partner API checkout endpoints buy as the key owner
POST /checkout/subscription (body tierId, creatorUsername, optional interval, successUrl, cancelUrl) and POST /checkout/video (body videoId, optional successUrl, cancelUrl) return { sessionId, checkoutUrl } for a hosted Stripe Checkout page. The buyer is always the account that owns the API key. There is no customerEmail or userId field, and sending an X-User-Token for a different user is refused 401 INVALID_USER_TOKEN (“Multi-user desktop flow via a shared pk_ key is currently disabled”).
Consequence: with one key for your site, every checkout would be bought by, and granted to, your own account — not to the viewer who clicked. These endpoints fit an app where each end user holds their own key; they cannot run a storefront for many viewers. POST /checkout/video also refuses a buyer account with no Stripe customer record yet (400, “Customer not found. Complete a platform purchase first.”). Both are origin-locked: see domain verification.
What works for a multi-viewer site today
- Let the embedded player sell. Paid videos are bought inside the player by email; subscriptions go through a DCAST sign-in. The money goes to the creator's connected Stripe account.
- Author access keys for people the creator wants to let in. Keys are created in the DCAST dashboard (videos: Content → Videos, access keys panel), one at a time or in bulk (up to 1000 per request). A key is a 16-character code with a seat rule (one screen at a time, or unlimited), an optional expiry and an optional redemption limit. The viewer types it into the embedded player, or your player passes it as
?accessCode=. The video pass accepts only keys scoped to that exact video. Keys cannot be created through the Partner API today. - Free membership for a known email:
POST /subscriberswith{ email, tierId? }creates an active subscription on a free tier (or with no tier), creating a DCAST account for that email if none exists. It opensSUBSCRIBERSvideos for that account once the viewer is signed in to DCAST as that email. Paid tiers are refused400 PAID_TIER_USE_CHECKOUT.
Reconciling sales
GET /purchases, GET /videos/:videoId/purchases and GET /monetization/payments list completed sales of your content (the last one needs a FULL_ACCESS key or finance:read). The purchase.completed webhook is sent to the creator when a POST /checkout/video session completes; purchases made inside the embedded player do not emit it today, so reconcile those from the list endpoints. subscription.created, subscription.renewed and subscription.cancelled are in the event catalogue.
5. Not available today
| Capability | Status |
|---|---|
| A Partner API method to grant a specific viewer (by email or id) access to a paid video, stream or tier | Not available. Planned — see Roadmap; no date. |
| Creating author access keys through the Partner API | Not available (dashboard only). Issuing a key per email is part of the same planned work; no date. |
Checkout on behalf of a viewer (customerEmail) over one key | Not available. |
Registration form for REGISTERED videos inside the embedded player | Not available. |
| Cross-creator search / trending / feed | Withdrawn (410). Not planned. |
6. Checklist
- Create a
FULL_ACCESSkey; keep it on your server. - Create tiers (
POST /tiers) and set each video'svisibility,isPaid/price,requiredTierId. - Make sure the creator's payout account is connected:
GET /monetization/connect-statusmust sayconnected: true. - Render the catalog from
GET /videos(orGET /channel/:username/videosfor public titles only). - Play with the embedded player for anything paid or members-only; use your own player only where
requiresTokenisfalseor you hold a code. - If your site calls
/checkout/*from its own pages, declare and verify its domain first. - Subscribe to webhooks and reconcile sales from the purchases endpoints.
