Tellyport Docs

Documentation

Build on Tellyport

Connect TV and mobile apps to a remote control plane: move backends, mock APIs, ship signed config safely, and operate your device fleet without shipping new builds.

Browse by topic

Launch your first app connection

You configure Tellyport once inside your app. After that, backend changes happen in the console, the CLI, or CI, without changing the app's permanent Tellyport URL.

  1. Add a backend server

    Open Backend servers and enter your current API base URL. No backend yet? Import an OpenAPI spec to mock it.

    Open servers
  2. Create an app route

    Choose proxy for live backend data or custom for your own JSON response.

    Open routes
  3. Create an app connection key

    Create a key for the app or platform. The raw value is shown only once.

    Open keys
  4. Add the SDK

    Pick your platform below, bundle a safe default config, and test against staging.

    View SDKs
  5. Run the risk check and publish

    Review what changes and who is affected before production clients receive it.

    Risk check

One permanent URL, movable infrastructure

Your app keeps one Tellyport address. Tellyport returns the currently published, signed configuration, or serves stable gateway routes that proxy to your backend or return mock responses.

Your appPermanent Tellyport URL
TellyportSigned live configuration
Your serverCurrent backend URL
Your app endpoints stay the same.

When a proxy route points to api_base_url, publishing a new base URL moves every attached path automatically. The replacement backend must still support the same request and response contract.

Plans & limits

Plans are based on apps and active devices. Change plans any time under Settings › Plan & billing.

Plans and included limits
PlanPriceAppsActive devicesHighlights
Free free$0 forever1500Community support
Starter starter$49/mo or $490/yr1 production5,000Versions & rollback, email support
Growth growth$299/mo or $2,990/yr5 production50,000Canary + auto-rollback, diagnostics, analytics, priority support
Agency agency$999/mo or $9,990/yrClient organizations250,000Advanced team controls, shared support channel
Enterprise enterpriseContact usCustomCustomSSO, approvals policy, dedicated data plane, custom retention, SLA
  • Active devices are devices seen in the last 30 days (they fetched config, registered, or sent a heartbeat).
  • Extra active devices on paid plans cost $8 per 1,000 per month, billed as a separate line item. Paid plans never block new devices.
  • On the Free plan, new registrations stop at 500 devices with 402 PLAN_DEVICE_LIMIT. Devices already registered keep receiving config.
  • Usage is shown in the console and returned by GET …/billing as usage: { activeDevices, includedDevices, overageUnits, overageAmount }.
  • Older plan names map automatically: sandbox → Free, pilot → Starter, fleet → Growth.

Change a backend without updating the app

Configuration mode

Edit api_base_url, save it to draft, run the risk check, and publish. The next config refresh returns the new URL.

Gateway mode

Attach routes to api_base_url. The permanent gateway paths stay the same when that base URL changes.

  1. Edit draft
  2. Risk check
  3. Test staging
  4. Publish
  5. Monitor
Compatibility matters

The new server must accept the same HTTP methods, paths, authentication, query parameters, bodies, and expected response shape.

Return your own response and data

Create a route, choose Custom JSON response, select an HTTP status, and enter any valid JSON. Path and query placeholders are resolved per request.

GET /profiles/:id?name=Aftab
{
  "success": true,
  "profile_id": "{{params.id}}",
  "greeting": "Hello {{query.name}}",
  "data": { "plan": "premium" }
}

Mock a backend from OpenAPI

Your app can ship before its backend exists. Import an OpenAPI document and Tellyport creates a custom (mock) route for each operation. When the real backend is ready, switch each route to proxy.

  1. Preview

    In Routes › Import OpenAPI, paste JSON or YAML. Preview shows the routes that would be created and anything skipped, and never writes.

  2. Apply

    Apply adds the routes to the environment. Response bodies come from example / examples first, otherwise they are synthesized from the schema (types, formats, enums, arrays of two items).

  3. Switch to proxy

    Point routes at a backend server one by one. The gateway path your app calls doesn't change.

POST …/environments/:env/routes/import-openapi
// request
{ "spec": "<OpenAPI JSON or YAML string>", "mode": "preview", "prefix": "/" }

// response
{
  "routes": [
    { "method": "GET", "path": "/questions/:id", "mode": "mock", "status": 200,
      "body": { "id": "q_1", "text": "string" }, "source": "example" }
  ],
  "skipped": [ { "method": "GET", "path": "/health", "reason": "Route already exists" } ],
  "applied": 0
}
Path parameters

OpenAPI {id} becomes :id (names are sanitized, so {item-id} becomes :item_id). Use "mode": "apply" to save; applied routes are ordinary custom routes. Existing method + path pairs are never overwritten, HEAD/OPTIONS/TRACE are skipped, and one import creates at most 200 routes.

Fetch signed runtime configuration

Use an app connection key only for bootstrap, gateway, and initial device registration. Never embed operator credentials, service tokens, or the system admin token in an app.

app.js
GET
Bootstrap config/api/v1/bootstrap/{project}/{environment}

Signed endpoints, app policy, maintenance, and features.

ANY
Stable gateway/gateway/{project}/{environment}/{routePath}

Mock response or proxy to the published origin.

POST
Register device/api/v1/devices/register/{project}/{environment}

Returns a short-lived device-scoped token.

POST
Device heartbeat/api/v1/devices/heartbeat/{project}/{environment}

Reports telemetry; may return diagnostics commands.

Mobile & web SDKs

Every SDK lives in the repository's sdk/ folder. Each fetches the bootstrap config, verifies its signature, keeps a last-known-good copy, retries with backoff, registers devices, sends heartbeats, polls, and builds gateway URLs.

Mobile and web SDKs
SDKPlatformsLocation
JavaScriptBrowser, Node 20+, web-based TV runtimessdk/originmesh-client.mjs
SwiftiOS 15+, tvOS 15+, macOS 12+sdk/swift
KotlinAndroid, Android TV, Fire TV, JVMsdk/kotlin
Dart / FlutterFlutter apps and pure Dartsdk/flutter
ContentView.swift
  • Signature check is on by default. The native SDKs verify the signed payload bytes and read the configuration only from them.
  • Revoked device tokens recover automatically in Swift, Kotlin, and Flutter: on a 401 the token is dropped and the request retries once with the client key. The JavaScript SDK does not do this yet.
  • Kotlin on older Android: built-in Ed25519 needs Android API 33+. On older versions, pass a Bouncy Castle verifier.

TV SDKs

TV apps are the hardest to update, so every major TV platform has a client. Each one keeps a last-known-good config on the device, reports heartbeats, and answers remote diagnostics requests.

TV platforms and their SDKs
PlatformSDKLocationConfig trust
RokuBrightScript Task node + helperssdk/rokuPinned HTTPS (Roku OS has no Ed25519)
Android TVKotlinsdk/kotlinEd25519
Fire TVKotlinsdk/kotlinEd25519 (Bouncy Castle below API 33)
Samsung TizenJavaScript + Tizen helpersdk/tizenEd25519 where supported, else HTTPS-only
LG webOSJavaScript + webOS helpersdk/webosEd25519 where supported, else HTTPS-only
Apple TVSwiftsdk/swiftEd25519 (CryptoKit)
MainScene.brs
  • Roku: copy sdk/roku/components/OriginMesh into your channel and include OriginMeshHelpers.brs in the scene. All network and registry work runs on the Task thread; observe its config field. Unset options fall back to the manifest keys originmesh_url, originmesh_project, originmesh_environment, and originmesh_key.
  • Tizen and webOS: older TV browsers have no WebCrypto Ed25519. By default the helpers then accept config only over https:// and mark it verifiedBy: "https"; pass verifySignatures: true to require Ed25519.
  • Country for the fleet map comes from the TV's locale setting when the edge doesn't provide it.

Your app survives Tellyport downtime

  1. Live signed configFrom Tellyport, or the edge's last good copy
  2. Last-known-good cachePreviously verified copy on the device
  3. Bundled defaultSafe config shipped inside the app

The SDKs support ETag refresh, cache only valid responses, and fall back without making app startup depend on the control plane.

Edge delivery

An optional Cloudflare Worker in edge/worker.js sits in front of /api/v1/bootstrap and /api/v1/signing-key. When the origin answers 5xx/429, times out, or is unreachable, it serves the last good signed response it stored.

Why it's safe

The edge never holds the signing key, so it can only replay config Tellyport really published. 401, 403, and 404 pass straight through, so revoking a key still works through the edge.

What changes in the app

Nothing. Keep the hostname baked into your apps on the Worker and move Tellyport to an origin hostname. Paths are identical.

  • Scoped copies: stored responses are keyed by URL and a hash of the caller's credential, and are only served to a caller presenting the same credential.
  • Visible: stale answers carry x-originmesh-edge: stale and a reason; copies older than STALE_MAX_AGE_SECONDS (default 7 days) are never served.
  • Deploy: copy edge/wrangler.toml.example, set ORIGIN_URL, optionally bind Workers KV for cross-region copies, then npx wrangler deploy. Full steps in edge/README.md.

Draft first, production second

Every edit goes into a draft. Publishing creates an immutable, signed version, so you can always see history or restore a known configuration.

Validate first

Check URLs, TLS expectations, and configuration shape.

Schedule a snapshot

Later draft edits cannot alter an already scheduled release.

Roll back safely

A rollback publishes the old snapshot as a new version.

Keep an audit trail

Actor, time, action, and reason are recorded.

Release risk check

Before publishing, Tellyport compares the draft with what devices run now and explains the impact in plain language: a level (low, medium, high), a score from 0 to 100, and the checks behind it.

Endpoint changes

Host changes, and endpoints that fail a health probe.

Forced updates

How many devices a raised minimum_version would force to update.

Disruptive switches

Maintenance mode turned on, features removed, route deletions.

Caching

Changes to cache_ttl that alter how fast devices pick things up.

POST …/environments/:env/releases/risk
{
  "level": "medium", "score": 48,
  "summary": "The API host changes for every device and 12 devices will be asked to update.",
  "impact": { "devicesTotal": 40, "devicesAffected": 40, "forcedUpdate": 12,
              "byPlatform": [ { "platform": "roku", "devices": 18 } ] },
  "checks": [ { "id": "endpoint_reachable", "status": "pass", "message": "api_base_url responded" } ],
  "changes": [ { "path": "endpoints.api_base_url", "before": "https://api.eu1…", "after": "https://api.eu2…" } ],
  "ai": { "available": false, "text": null }
}

Checks: config_valid, endpoint_host_changed, endpoint_reachable (probes new or changed endpoint URLs), forced_update, maintenance_enabled, features_removed, cache_ttl, and route_deletions when you send the proposed routes. The level is high if any check fails or the score reaches 50, and medium from 20.

Deterministic first

The checks are rules, not guesses. If an AI summary is configured on the server it's added as ai.text; if it isn't available, the risk check still works.

Release to a few devices first

A canary sends a new version to a share of your devices, watches their telemetry, and stops itself if those devices start failing.

  1. Start the canary

    In the publish dialog, choose Release to 5–50% of devices first.

  2. Watch and widen

    Under Deployments, raise the percentage, choose Release to everyone, or Stop canary.

  3. Let safety act

    If canary devices error too often, Tellyport stops the canary automatically.

Who is in the canary?

Devices are picked by a stable hash of their device ID, so a device stays in the canary as the percentage grows. Devices that send no device ID stay on the stable version. Only one canary runs at a time, and a rollback or scheduled publish stops it.

Automatic rollback

The worker compares heartbeat errors (playerState: "error" or an errorCode) from canary devices with stable devices over the last 15 minutes. When enough canary devices report and their error rate passes the threshold (default 20%, and at least 5 points above stable), it stops the canary, records it in the audit log, and sends canary.rolled_back to webhooks and alerts.

Tune it

Adjust the threshold and minimum device count under Deployments › Release safety.

Require a second pair of eyes

Approvals are off by default, which suits one-person teams. Turn on Require a teammate to approve every publish when you want a review.

  • Publish draft then opens a change request instead of publishing.
  • A different teammate with publish permission approves or rejects it under Deployments. The publish button then goes live with the approved revision.
  • Editing the draft after approval invalidates the approval. That includes drafts saved by the CLI or GitHub Action.
  • Rollback never waits for approval, so incidents can always be undone.
Some edits apply immediately

Endpoint, flag, route, and device-override edits still take effect immediately and require publish permission in production.

Config as code

Keep each environment's config in your repository, review changes as diffs in pull requests, and release them from CI with the same rules as the console. The CLI is a single dependency-free Node script: cli/originmesh.mjs (Node 18+).

Terminal
node cli/originmesh.mjs login                    # URL + service token, saved to ~/.originmesh/config.json
node cli/originmesh.mjs pull -p tv-app           # writes originmesh.json (draft config + routes)
$EDITOR originmesh.json
node cli/originmesh.mjs diff                     # what would change in the draft
node cli/originmesh.mjs push -m "Move API to eu2"   # saves the draft, never publishes
node cli/originmesh.mjs publish --reason "Move API to the eu2 cluster" --rollout 10
CLI commands
CommandWhat it does
pullExports the draft to --file (default originmesh.json; a .yaml file pulls YAML).
diffLists changed paths. --exit-code exits 1 when there are changes.
pushSaves the file as the draft. --revision n fails if the draft moved since you pulled.
publishThe console's publish flow: validation, approvals (--change-request <id>), and canaries (--rollout 10). Production needs a --reason of at least 10 characters.
import-openapi <file>Previews mock routes from an OpenAPI spec; --apply creates them, --prefix /v1 prefixes paths.
  • In CI, set ORIGINMESH_URL and ORIGINMESH_TOKEN instead of running login. Project and environment come from --project/--env, the file, or ORIGINMESH_PROJECT/ORIGINMESH_ENV.
  • Exit codes: 0 success, 1 changes found (diff --exit-code), 2 error. Add --json to any command for raw API output.

The file

originmesh.json
{
  "project": "tv-app",
  "environment": "production",
  "version": 51,
  "config": {
    "endpoints": { "api_base_url": "https://api.eu2.streambox.tv" },
    "app": { "minimum_version": "3.2.0", "force_update": false },
    "maintenance": { "enabled": false },
    "features": { "new_home_enabled": true },
    "cache_ttl": 300
  },
  "routes": [
    { "name": "Catalog item", "method": "GET", "path": "/catalog/:id", "mode": "proxy", "upstreamKey": "api_base_url", "enabled": true }
  ]
}
GET
Export…/environments/:env/config/export?format=json|yaml

The draft document, including draftRevision.

POST
Import…/environments/:env/config/import

mode: "diff" (default) lists changes; mode: "apply" saves the draft and returns draftRevision.

  • Routes are optional. If the document has routes, apply replaces the environment's routes with that list. Routes are live, so in production that needs publish permission. Leave routes out to keep them as they are.
  • Conflicts: send revision to make apply fail with 409 REVISION_CONFLICT if someone changed the draft since your export.
  • Change paths look like config.endpoints.api_base_url or routes.GET /catalog/:id.

GitHub Actions

Copy cli/github-action.example.yml to .github/workflows/originmesh.yml and cli/originmesh.mjs to tools/originmesh.mjs. Pull requests get a diff; pushes to main push the draft and publish it.

.github/workflows/originmesh.yml
name: Tellyport config
on:
  push: { branches: [main], paths: [originmesh.json] }
  pull_request: { paths: [originmesh.json] }

jobs:
  originmesh:
    runs-on: ubuntu-latest
    env:
      ORIGINMESH_URL: ${{ vars.ORIGINMESH_URL }}
      ORIGINMESH_TOKEN: ${{ secrets.ORIGINMESH_TOKEN }}
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 22 }
      - run: node tools/originmesh.mjs diff --file originmesh.json
      - if: github.event_name == 'push'
        run: node tools/originmesh.mjs push --file originmesh.json --message "$COMMIT_TITLE"
        env: { COMMIT_TITLE: "${{ github.event.head_commit.message }}" }
      - if: github.event_name == 'push'
        run: node tools/originmesh.mjs publish --file originmesh.json --reason "GitHub $GITHUB_REPOSITORY@${GITHUB_SHA::7}"
Approvals still apply

If the environment requires a teammate's approval, drop the publish step and publish from the console, or pass --change-request <id> for an approved request. Add --rollout 10 to start as a canary.

Service tokens

Service tokens give CI access to named projects with the editor or viewer role. Only owners can create them (Organization settings › Service tokens), and the token is shown once.

POST /api/v1/organizations/:org/tokens
{ "name": "github-ci", "role": "editor", "projects": ["tv-app"] }

→ 201 { "token": "om_st_…", "id": "…", "name": "github-ci", "prefix": "om_st_ab12cd",
        "role": "editor", "projects": ["tv-app"] }

# use it on project routes
Authorization: Bearer om_st_…
  • Scope: projects: [] means every project in the organization. Tokens get 403 on anything outside project routes.
  • Roles: editor can view, update, and publish config; viewer is read-only.
  • Manage: GET on the same path lists tokens with lastUsedAt; DELETE …/tokens/:id revokes one immediately. Actions appear in the audit log as Service token <name>.

Analytics & fleet map

Each environment records hourly buckets of bootstrap requests, 304s, errors, gateway traffic, and distinct devices, kept for 30 days.

GET
Analytics…/environments/:env/analytics?range=24h|7d|30d

Time series (hourly for 24h and 7d, daily for 30d) plus totals (requests, errors, active devices, error rate, p95 latency) and breakdowns by platform, app version, and config version.

GET
Fleet map…/environments/:env/fleet/map

Devices, online devices, and devices reporting errors per country.

Where country comes from

From edge headers (cf-ipcountry, x-vercel-ip-country, x-country) on register and heartbeat when present, otherwise the country the SDK sends from the device locale. Devices without either are counted as unknown.

Remote diagnostics

Ask a specific device for its recent logs, app state, and a screenshot. The request is delivered on the device's next heartbeat and the SDK uploads the result.

  1. Request

    Open a device and choose Request diagnostics, or call POST …/devices/:deviceId/diagnostics with { "include": ["logs","state","screenshot"] }.

  2. Deliver

    The heartbeat response carries commands: [{ "type": "diagnostics", "id": "diag_…", "include": [...] }].

  3. Upload

    The SDK posts to /api/v1/devices/diagnostics/:project/:env/:requestId with its device token. The request shows completed.

  • Limits: up to 500 log lines, 2 MB per upload, PNG or JPEG screenshots only. Requests expire after 24 hours; the console lists the latest 20. Requests and uploads are audited.
  • Screenshots depend on the platform. Where an app can't capture its own screen (for example Tizen web apps), the result says screenshotUnsupported: true.
  • Your own context: SDKs let you add log lines and a state object, such as the current screen and player state.

Studio: build TV & OTT apps without a new build

Studio is a visual editor at /studio/. The design you publish travels inside the signed configuration, so every TV running the Studio app template renders it on its next check-in, with canary, rollback, approvals and config as code included.

  1. Choose the kind of app and a template

    Streaming, IPTV player, Sports, News, Education, Restaurant menu, Hotel lobby signage, Gym schedule, Community board, Photo frame, Quiz night, Live poll, Kids storytime, or start blank.

  2. Brand and content

    Name, logo, colours and fonts with a live preview. Point rows at a JSON feed, MRSS, RSS, YouTube channel, static items, or viewer-provided playlists.

  3. Edit on the canvas

    Click a block in the TV preview to select it, drag to reorder, press + between blocks to add one. The inspector on the right edits the selected block, screen, theme or source.

  4. Save and publish

    Save a draft, run the risk check, publish with a reason. Ship the app template once per platform; afterwards every change is a publish, not a store update.

Any kind of TV app

The wizard starts with What are you building? Each kind sets sensible defaults and a shortlist of blocks in Simple mode; everything stays available in Advanced mode.

  • Streaming & media: movies, series, live TV, radio, podcasts, YouTube.
  • Business & info: restaurant menus, hotels, clinics, schools, community centres, company TV.
  • Digital signage: a hands-free loop of pages for lobbies, shop windows and waiting rooms.
  • Games & interactive: quiz nights, polls, scoreboards and prize wheels driven by the remote.
  • Utility (clock, weather, tickers, photo frame) and Kids & learning (big friendly buttons).

Blocks

  • Content & promo: hero, rail, grid, banner, text, carousel, categories, video hero, countdown, QR code, button row.
  • Business: menu & prices, schedule, contact & hours, notice. Signage: ticker, slideshow, clock, weather (Open-Meteo, no key).
  • Games (plugin pack): quiz, poll, leaderboard, scoreboard, spin-the-wheel. Scores stay on the TV; results can be posted to your own API.
  • Layout & advanced: tabs, columns, spacer, custom HTML/CSS (sandboxed, no scripts), plugin blocks registered by your app build.

Sources

Static lists, JSON feeds, MRSS, RSS, YouTube channels, M3U/Xtream/XMLTV (viewer-provided), and new in v3: CSV (a Google Sheet published to the web: menus, schedules, any list) and iCal calendars. Feeds without CORS are fetched through the Tellyport feed proxy, which only allows URLs that are part of the design.

Endless possibilities

  • Rules (visibleIf): show a block only on certain platforms, countries, locales, rollout groups, feature flags, or during a time window.
  • Languages: any text can be a per-locale map; the TV picks its own language and switches to right-to-left where needed.
  • Experiments: A/B overrides by rollout group. Assets: a reusable library referenced as @asset:key.
  • Theme extras: gradients, background images, logo position, card shadows, custom CSS.
  • JSON view: edit the whole document as JSON, import/export it, restore a design from any published version, and compare the draft with what is live.
Bring your own playlist

IPTV sources are always entered by the viewer on the device and stored only there. A Studio design never contains channel lists, playlist URLs or panel credentials, and the validator rejects any that are included.

GET
Templates…/environments/:env/studio/templates

The starter designs with their feature tags.

GET
Hosted TV app/studio/web-tv/?project=…&environment=…&key=…

The web template for Samsung Tizen, LG webOS, Android TV, Fire TV and browsers. Package it with studio/templates/web-tv/tools/package.mjs.

Publish to TV stores

Studio › Stores turns a published design into installable packages for each TV platform, creates the artwork every store asks for, and walks you through each store console step by step.

  1. Listing

    Name, descriptions, category, age rating, privacy policy, version and the store ids (Android application id, Samsung package id, LG app id). One listing feeds every store.

  2. Artwork

    One click creates every icon, banner and background at the exact size (Samsung 512×423, LG 80/130 + splash, Android launcher icons and the 320×180 TV banner, Play, Amazon, Roku and Apple TV art) from your brand colours and logo. Replace any with your own file: Studio fits it to the size.

  3. Screenshots

    Capture the TV preview straight from Studio (the browser asks which tab to share) or upload your own; they are stored at 1920×1080.

  4. Builds

    Each package ships the published design (shown offline before the first check-in) and its own client key, which you can revoke under Connect › Keys. After install the design keeps updating from Tellyport; rebuild only to change the name, icon or version.

  5. Store steps

    Checklists for Google Play, Amazon Appstore, Samsung Seller Office, LG Seller Lounge, the Roku Channel Store, App Store Connect (Apple TV) and web hosting, with links to each console. Progress is saved on the listing.

.wgt
Samsung (Tizen)

Unsigned: unzip, then tizen package -t wgt -s <profile> with your Samsung certificate before uploading.

.ipk
LG (webOS)

Install on a TV in Developer Mode with ares-install; submit in LG Seller Lounge.

.zip
Android TV / Google TV + Fire TV

A Gradle project with a native WebView shell. ./build.sh writes the APK (Fire TV, sideloading) and the AAB (Google Play). Set a website URL in the listing to wrap that site full screen instead of the Studio design.

.zip
Roku

A native SceneGraph channel (no web view). Sideload it on a Roku in Developer Mode, then package and sign it on the device for the Roku Channel Store. See what Roku draws.

.zip
Web / kiosk

Static files for any HTTPS host or signage player.

.zip
Apple TV (tvOS)

A native SwiftUI Xcode project with the Swift SDK, your design and the layered icons. Open it on a Mac, choose your team, run it, then Archive and upload to App Store Connect. See what Apple TV draws.

What each TV draws

Samsung, LG, Android TV, Fire TV and web run the full web-TV runtime, so every block works there. Roku (SceneGraph) and Apple TV (SwiftUI) are native apps that draw the subset below. Studio shows the same chips on every block, screen and source, and the Roku and Apple TV build cards list what your design uses that they skip. Anything not drawn is left out; the rest of the screen still works.

Studio features by renderer
FeatureSamsung · LG · Android TV · Fire TV · WebRokuApple TV
Blocks
hero✓✓✓
video-hero✓◐ Shows the poster and title; OK plays the video full screen (no muted background preview).◐ Shows the poster and title; OK plays the video full screen (no muted background preview).
rail✓✓✓
carousel✓✓✓
grid✓✓✓
categories✓◐ Groups open a grid of their items; the chips and list styles draw as tiles.◐ Groups open a grid of their items; the chips and list styles draw as tiles.
banner✓✓✓
text✓✓✓
button-row✓✓✓
spacer✓✓✓
countdown✓✓✓
tabs✓✓✓
columns✓✓✓
qr✓◐ Shows the link as text: Roku has no QR drawing.✓
custom✓— Custom HTML needs a web view.— Custom HTML needs a web view.
plugin✓— Plugins (games, polls) run on the web-TV runtime only.— Plugins (games, polls) run on the web-TV runtime only.
embed✓— Embedded web pages need a web view.— Embedded web pages need a web view.
menu✓✓✓
schedule✓✓✓
ticker✓✓✓
slideshow✓✓✓
clock✓✓✓
weather✓✓✓
contact✓◐ The QR code shows as the link.✓
notice✓✓✓
Screens
page✓✓✓
details✓✓✓
grid-browse✓✓✓
signage✓✓✓
game✓◐ Draws its blocks; plugin games stay web-only.◐ Draws its blocks; plugin games stay web-only.
player✓◐ Videos play full screen from any play action; a standalone player screen is not drawn.◐ Videos play full screen from any play action; a standalone player screen is not drawn.
live-tv✓——
search✓——
settings✓——
onboarding✓——
profiles✓——
web✓— Roku has no web view: link the site with a QR or text instead.— Apple TV has no web view: link the site with a QR code instead.
Sources
static✓✓✓
json-feed✓✓✓
mrss✓✓✓
rss✓✓✓
m3u✓✓✓
csv✓✓✓
ical✓——
xtream✓——
xmltv✓——
youtube-channel✓— YouTube cannot play inside a Roku channel.— YouTube cannot play inside a tvOS app.
Legend

✓ drawn, ◐ drawn with the difference noted, — not drawn on that TV.

Send releases to the stores

Studio › Stores › Release connects your store accounts once, then sends the app package, the listing text and the artwork in one click. Samsung, LG and Roku have no public submission API, so they keep their guided Store steps.

What each store API updates
StoreKey you addWhat Tellyport sends
Google PlayService account JSON (Play Console › Setup › API access, role “Release apps”)AAB or APK to a track (internal, closed, open, production; draft or completed), title and descriptions, icon, feature graphic, TV banner and TV screenshots, then commits the edit.
Amazon AppstoreLogin with Amazon security profile (client ID + secret) and the app IDReplaces the APK in the open edit, updates the listing, and optionally submits for review.
App Store ConnectAPI key: issuer ID, key ID and the .p8 file (App Manager role)Creates the tvOS version if needed and updates description, keywords, support URL, name, subtitle and privacy URL. The build is uploaded from Xcode (Product › Archive).
  1. Connect

    Paste or choose the key file and press Test connection. Keys are encrypted at rest, never shown again and can be replaced or removed at any time.

  2. Upload the package

    Run ./build.sh in the Android TV package and upload the .aab (Play) or .apk (Amazon). Uploads are kept for 7 days.

  3. Send

    Choose the store, the track and what to send. The release runs on the server; its steps and any store error appear in Release history. A failed Play release discards its edit, so nothing half-done reaches the store.

Permissions

Connecting stores and sending releases need config.publish and a verified email. Production releases on Google Play need an explicit confirmation. Every connection and release is in the audit log.

Manage your content in one place

Studio › Content keeps collections of videos, live channels, menu items, classes, slides or news for this project. Every TV app (Samsung, LG, Android TV, Fire TV, Roku and Apple TV) reads them as a feed, so content changes need no new design and no new app build.

  1. Create a collection

    Pick a kind (videos, live channels, menu, schedule, slides, news). The kind decides which fields the editor shows.

  2. Add or import items

    Add items one by one (with image upload), or import a CSV (Google Sheets: File › Download › CSV), JSON, M3U playlist or RSS feed. Preview shows what will be imported and which rows are skipped.

  3. Use it in the design

    “Use in design” adds the collection as a source. Choose it in any rail, grid, carousel, menu, schedule or slideshow and publish the design once.

  4. Publish, schedule, reorder

    Drafts never reach TVs. Published items appear within about a minute; “Show from” and “Show until” put items on air for a time window (offers, events). Up/down arrows set the order on screen.

GET
/api/v1/library/{project}/{collection}

The public feed: published items in order, in the Studio item model (title, description, image, stream, group, genres, price, start, end, location…). Cached with an ETag; edits clear the cache straight away.

POST
/api/v1/projects/{project}/environments/{env}/library/{collection}/items

Add items from your own tools (up to 500 per call, 5000 per collection). Import, bulk publish/unpublish/delete and reorder have their own routes.

Who can do what

Editors (config.update) add and change drafts. Publishing, unpublishing and changing anything already on air needs config.publish, like publishing a design. Every change is in the audit log.

Give everyone the right controls

All people use the same login page. Their organization membership and role decide which data and actions the server allows.

Roles and what they can do
RoleScopeMain controls
Super AdminAll organizationsProvision agencies, manage owners and teams, access every project.
OwnerOwn organizationFull project and team control. Cannot promote another Owner.
AdminOwn organizationProjects, publishing, keys, devices; team is read-only.
DeveloperOwn organizationEdit drafts and devices; no production publishing.
Release ManagerOwn organizationPublish, rollback, schedules, and emergency controls.
ViewerOwn organizationRead-only configuration, device, key, and audit visibility.
Safety protections

Users cannot disable themselves, the last active Owner cannot be removed, cross-organization access is blocked, and role changes sign out the affected member immediately.

Sign-in, email, and invitations

Verify your email

New accounts confirm their email before publishing, creating client keys, or inviting teammates. Accounts that existed before this release count as verified.

Reset a password

Click Forgot password? on the sign-in page and Tellyport emails you a reset link.

Invite teammates

Owners invite people from Team access › Invite by email.

Switch organizations

Someone who already has an account accepts the invitation while signed in, then switches organizations from the header.

For administrators

Set RESEND_API_KEY, MAIL_FROM, and ORIGINMESH_PUBLIC_URL before launch. Without a mail provider in production, verification, reset, and invitation emails are not sent.

Turn on two-factor authentication

Anyone can protect their account with an authenticator app.

  1. Open Account settings

    Add the setup key shown to your authenticator app (or open the link on this device), then enter the 6-digit code to turn it on.

  2. Save your recovery codes

    Eight single-use recovery codes are shown once. Store them somewhere safe, such as a password manager.

Lost your phone and your codes?

An owner must disable the account and invite you again as a fresh account. Administrators: ORIGINMESH_ENCRYPTION_KEY encrypts authenticator secrets, so back it up with the signing key.

Set up single sign-on (OIDC)

Let your team sign in through your company identity provider. Configure it under Team access.

  1. Register the callback URL

    Copy the callback URL shown in Tellyport into your identity provider's app settings.

  2. Add your email domains

    Add each email domain your team signs in with, and choose the default role for new SSO users.

  3. Prove you own the domain

    Publish the DNS TXT record shown in the console, then click Check DNS.

DNS TXT record
_originmesh-challenge.example.com.  TXT  "originmesh-verification=<token shown in the console>"
Good to know

A domain verified by one organization cannot be claimed by another. Super-admin accounts can never sign in through SSO. SSO client secrets are encrypted with ORIGINMESH_ENCRYPTION_KEY.

Get alerts in Slack or PagerDuty

Go to Webhooks › Slack and PagerDuty alerts, add a destination, and click Send test to confirm it works.

Slack

Paste an incoming webhook URL from your Slack workspace.

PagerDuty

Paste an Events API v2 integration key. Incidents for an endpoint resolve automatically when it recovers.

Alerts cover endpoints going down and recovering, canary rollbacks, publishes, rollbacks, and change requests awaiting approval. Alert destinations are stored encrypted.

Scrape Prometheus metrics

Set ORIGINMESH_METRICS_TOKEN on the server, then scrape /metrics with that value as a bearer token.

prometheus.yml
scrape_configs:
  - job_name: originmesh
    scheme: https
    metrics_path: /metrics
    authorization:
      type: Bearer
      credentials: <ORIGINMESH_METRICS_TOKEN>
    static_configs:
      - targets: ["config.example.com"]

It reports request counts and latency, bootstrap responses by release, sign-ins, publishes, gateway upstream errors, devices online, canary share, and outbox backlog. Each API instance reports its own counters, so scrape every instance.

Verify the audit log

Each project's audit log is a hash chain. Open Audit log and click Verify integrity to check it.

Chain intact

No stored entry was edited or removed outside Tellyport.

Chain broken

An entry was edited or removed outside Tellyport. Preserve logs and investigate.

Limit

Removing only the newest entries cannot be detected by the chain alone, so keep database backups.

Common errors and what to do

401Invalid credential

Check the app key, service token, environment, expiry, or device token.

403Permission or organization denied

Confirm the user's or token's role, projects, and selected organization.

409Draft revision conflict

Reload the latest draft before applying the change again.

422Validation failed

Read the response message and correct the submitted fields.

429Rate limited

Wait for Retry-After, then retry with backoff.

502/504Backend unavailable

Check DNS, TLS, health path, timeout, and failover server.

Data recovery

Never run docker compose down -v. Keep PostgreSQL backups off-host and test restoration regularly. Normal rebuilds preserve the named database volume.

Common questions

Does the app need only one Tellyport URL?

Yes. Store your permanent Tellyport host (or the edge hostname) and the project/environment path. Backend addresses are then resolved remotely.

Will existing endpoints stay the same after changing the base URL?

Yes for Tellyport gateway routes. The new server must implement the same API contract used by those routes.

Can I start before my backend exists?

Yes. Import an OpenAPI spec to create mock routes, or create custom routes by hand, then switch them to proxy when the backend is live.

Where do agencies and their teams sign in?

Everyone uses the same login page. Organization membership and role automatically scope the console and API.

Can an update delete my data?

Application rebuilds preserve the PostgreSQL named volume. Data can still be lost without backups or if the volume is explicitly deleted, so keep verified off-host backups.

Ready to build?

Make your first connection

Use the guided checklist in the console, or explore the complete API contract.