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
- Other platforms have their own guides: Android, iOS, Web / React Native.
- New to the concepts? Read How SubSovereign works.