SubSovereign
All guides

SubSovereign on Roku — integration guide

This guide takes your Roku channel from "I have no idea who paid me" to "my channel unlocks the right features for the right user, verified on my own server." The SDK is a single BrightScript file and assumes no prior experience with subscription tooling.

What SubSovereign does for you

Your users subscribe through Roku Pay (the Roku Channel Store). SubSovereign answers one question for your channel, reliably: what has this user actually paid for?

  • Your channel asks SubSovereign for the user's entitlement — the access they've unlocked.
  • The receipt validation happens on the server, directly with Roku, so a tampered client can't fake a subscription.
  • It is self-hosted: it runs on your infrastructure, your users' data stays with you, and there is no revenue share — you keep 100% of what your users pay.

You never trust the device. The device asks; the server decides.

Before you start

You'll need a running SubSovereign server, an app registered in the dashboard (giving you an appId and an API key), and your access levels (tiers, e.g. pro) created and linked to your Roku Pay products. Set up the products in the Roku Developer Dashboard and handle the purchase with roChannelStore as normal — SubSovereign verifies and records it afterwards.

Step 1 — Add the SDK

Drop SubSovereign.brs into your channel's source/ folder. That's it — no package manager. Its HTTP calls use roUrlTransfer with the Roku certificate bundle already wired up.

Step 2 — Configure once, at channel start

Call SubSovereign_Configure early (e.g. in your Main), passing your API key, app ID, a stable user identifier, and your server URL:

SubSovereign_Configure("YOUR_APP_API_KEY", "your-app-id", userId, "https://subs.yourdomain.com/api/v1")

Use the same userId every time for a given user so their access follows them across devices. (The config lives on m, so configure on the same thread you'll call from — see the note in Step 6.)

Step 3 — Check what the user can access

SubSovereign_CheckEntitlements() returns an object. Read hasAccess for the quick yes/no:

result = SubSovereign_CheckEntitlements()
if result.hasAccess = true
    unlockProFeatures()
else
    showFreeExperience()   ' free tier, or send them to the Channel Store
end if

If the request fails, the object comes back with hasAccess = false and an error field — so a network blip fails closed (locked), never accidentally unlocked.

Step 4 — Sell a subscription

Run the purchase through Roku Pay (roChannelStore) as normal. When Roku returns a successful transaction, pass its transactionId (with the product ID and the access level it grants) to SubSovereign so the server validates it directly with Roku:

result = SubSovereign_ValidateRokuPurchase(transactionId, productId, "pro")
if result.granted = true
    entitlements = SubSovereign_CheckEntitlements()   ' re-check, then unlock
    unlockProFeatures()
end if

The purchase is only real once the server has confirmed it with Roku.

Step 5 — Feature flags and consent

Roll features out from the server without a channel update:

flags = SubSovereign_GetFeatureFlags()
if flags.newPlayer = true then showNewPlayer()

Capture GDPR consent (fire-and-forget):

purposes = { analytics: true, marketing: false }
SubSovereign_RecordConsent(purposes, "GDPR")

Step 6 — Run network calls off the render thread

SubSovereign_CheckEntitlements and SubSovereign_ValidateRokuPurchase wait for the server (up to 15 seconds). Never call them on your render/UI thread — a slow network would freeze the screen. Run them inside a Task node and pass the result back to your scene:

' in a Task node's function:
SubSovereign_Configure(m.top.apiKey, m.top.appId, m.top.userId, m.top.baseUrl)
m.top.result = SubSovereign_CheckEntitlements()   ' observed field the scene listens on

The fire-and-forget call SubSovereign_RecordConsent doesn't wait for a response and is safe to call directly.

Best practices

  • Check on channel launch so gating is right before the user reaches locked content.
  • Re-check after buying so the UI reflects new access immediately.
  • Never trust the client — ask the server; it verified the transaction with Roku.
  • Always use a Task node for the entitlement and validation calls.

Quick reference

You want to… Call
Set the SDK up SubSovereign_Configure(apiKey, appId, userId, baseUrl)
See what the user unlocked SubSovereign_CheckEntitlements() → object with hasAccess
Verify a Roku Pay purchase SubSovereign_ValidateRokuPurchase(transactionId, productId, accessLevel)
Read feature flags SubSovereign_GetFeatureFlags()
Record GDPR consent SubSovereign_RecordConsent(purposes, jurisdiction)

Next steps