Put your content on every screen with a single code
TEVIX is an app for Smart TVs, phones and computers that becomes your company's app. You don't need to publish anything in the app stores: your audience installs TEVIX, enters your code, and from then on the device always opens straight into your content, with your name, your color and your logo.
No app development
Works for news portals, radio stations, local TV, churches, video players, live cameras, any website.
Your content stays with you
Video, audio, images and pages are loaded directly from your server. TEVIX only delivers the code to the devices.
One code, every device
Samsung, LG, Android TV, Fire TV, phone, computer and browser. Remote control, touch and keyboard.
In 3 steps
Create your code
On the sign-up page, enter a name, a color, the URL of your content and a contact. Your code is issued instantly, with a free trial.
Test
Open TEVIX, enter the code and check it on TV, phone and computer. Adjust the URL or the look in the partner area whenever you like.
Spread the word
"Install TEVIX and enter ABC-123". The partner area generates ready-made text for your audience.
Choose your source type
The "source" is where TEVIX fetches your content from. There are three types. Start with the simplest one for your case; you can switch later without changing your code.
| Type | When to use it | What you need | How it looks |
|---|---|---|---|
| Website | You already have a website, web player or dashboard that works well on TV. | Just the URL (e.g. https://player.yourradio.com). | Full screen, with no TEVIX menu. Same as opening the site in the TV's browser. |
| Compatible | You already have a start.json from another TV application. | The URL of your start.json. | TEVIX reads your menu and pages and renders them with the TEVIX look. Nothing to redo. |
| TEVIX | You want an app with a menu, featured items, news grids, videos and a persistent radio player. | JSON files in the format below, hosted on your server. | Side menu, cards, article reader, radio player. |
Website or web player
Enter the full URL. The device opens that page full screen inside TEVIX. It works for any website, but to look good on TV the site needs to support arrow-key and OK navigation (visible focus on buttons). If it already works in the TV's browser, it will work here.
http:// (no HTTPS)? It works the same. TEVIX notices the content is http and switches to its own http address so your player opens embedded, with the Back button. You do not need to change anything on your server.http://, both http:// and https:// sites work. Once TEVIX moves to https://, an http:// site will still open, but through direct navigation instead of embedded. Always prefer HTTPS on your site.Compatible format (start.json)
If you already have a start.json published for another TV application, register its URL (for example https://yoursite.com/app/start.json). TEVIX understands:
parameterwithmenu:,content:andredirect:; the{PREFIX}and{SERVER}placeholders are filled with the protocol and host of your start.json.- Menu (
headline,menu[]withlabel,icon,dataas a URL or object,execute,focus). - Content in
pages[].items[]withlayouton the 12×6 grid (or 16×8 withcompress), as well astemplate+itemswith{context:...}andaction: "video:data". - Item types
default,separate,teaser,button,space; fieldstitle,titleHeader,titleFooter,label,image,icon,action,execute,focus. - Actions
video:,audio:,link:,link:window:,content:,info:,reload:,back:and multiple actions[a|b].
Not yet supported in this format: plugins (video:plugin:, audio:plugin:), panel:, request:interaction: and extended properties. These items show a notice instead of breaking.
TEVIX format
This is the most complete format, and a simple one too: a main file (tevix.json) with your identity and menu, plus one file per page. Details below.
tevix.json: identity and menu
{
"tevix": "1.0",
"app": {
"name": "Portal Cidade",
"tagline": "News and Radio 98.7",
"short": "PC",
"color": "#E8452C",
"logo": "logo.png"
},
"menu": [
{ "id": "inicio", "label": "Home", "icon": "home", "content": "inicio.json" },
{ "id": "aovivo", "label": "Live", "icon": "live", "content": "aovivo.json" },
{ "id": "noticias", "label": "News", "icon": "news", "content": "noticias.json" },
{ "id": "radio", "label": "Radio", "icon": "radio", "content": "radio.json" }
],
"player": {
"type": "audio",
"title": "Radio Cidade 98.7 FM",
"subtitle": "Live",
"url": "https://stream.suaradio.com.br/live.mp3"
}
}
| Field | Required | Description |
|---|---|---|
app.name | yes | Name shown in the menu and in the title. Up to 40 characters. |
app.tagline | no | Line below the name. |
app.short | no | 2-letter abbreviation used when there is no logo. |
app.color | no | Brand color in hexadecimal. Colors the focus, buttons and player. |
app.logo | no | Square image (min. 128×128). Absolute URL or relative to tevix.json. |
menu[] | yes | 1 to 8 items. Each with label, icon and content (page URL or the page embedded as an object). |
player | no | Persistent radio at the bottom of the screen. Stream url (mp3/aac). autoplay only works where the device allows it. |
app section of tevix.json. That way you can change the color without touching your files.Pages and blocks
Each menu item points to a page file. A page has a title and a list of blocks, from top to bottom.
{
"title": "Home",
"meta": "Updated 4 min ago",
"blocks": [
{ "type": "hero", "items": [ "1 large featured item + up to 2 on the side" ] },
{ "type": "row", "title": "Latest videos", "items": [ "horizontal carousel" ] },
{ "type": "grid", "title": "News", "items": [ "grid of cards" ] },
{ "type": "list", "title": "Most read", "items": [ "numbered text-only list" ] }
]
}
| Block | Purpose | Items |
|---|---|---|
hero | Top highlight (headline, live). | The 1st is large; the 2nd and 3rd sit beside it. Use 2:1 images for the first one. |
row | Horizontal carousel (recent videos, shows). | Any number; scrolls sideways. |
grid | Grid of cards with images (news, channels). | Any number; wraps into rows. |
list | Compact title-only list (rankings, schedule). | Up to ~30 per block to look good on TV. |
Items
{
"k": "Sports",
"title": "Cidade FC wins and takes the lead",
"desc": "A goal in the 88th minute secured the win.",
"time": "1 h ago",
"image": "https://seusite.com/img/futebol.jpg",
"action": "text:materia/1235.json"
}
| Field | Description |
|---|---|
title | Card title (required). Up to 2 lines on TV: keep it under 70 characters. |
k | Short tag in the corner of the image (section, "Live", "Podcast"). |
desc | Summary of up to 2 lines. |
time | Small text (time, duration, "22 min ago"). |
image | 16:9 image (ideally 640×360 or 960×540, JPG). Without an image, the card uses a neutral background. |
action | What happens when the item is selected. See below. |
Actions
| Action | What it does | Notes |
|---|---|---|
video:URL | Opens the full-screen video player. | MP4 (H.264) or HLS (.m3u8). HLS is ideal for live streams. |
audio:URL | Plays in the persistent radio player without leaving the page. | MP3/AAC, stream or file. Play/pause with OK. |
text:URL | Opens an article in reading view. | URL of a JSON file in the article format. |
content:URL | Opens another page (page JSON) with a Back button. | For full schedules, subcategories, etc. |
link:URL | Opens a website full screen inside the app. | Back closes it and returns to TEVIX. |
link:window:URL | Opens the website in a new window (outside the app). | Only makes sense on a computer. |
info:text | Shows a quick notice. | E.g. "Call 0800 123 987". |
content:plugin:ID, video:plugin:ID, panel:…, player:… | Content and player driven by your own plugin. | See Plugins. |
URLs can be relative to the file they appear in (materia/1235.json, ../videos/x.mp4). An item without an action is informational only.
Plugins: dynamic content from your server
Static JSON files cover most apps. When the content needs real-time logic (a schedule that changes, a player with login, live progress, a per-user catalog), use a TEVIX plugin: an HTML page of yours that runs hidden inside the app and talks to it. The app asks the plugin for content, the plugin answers with the same page JSON you already know and, whenever you want, updates items on screen, opens a video, shows a panel or reacts to what the user does.
Same format
The plugin returns pages, items and actions identical to those in the JSON files. Nothing new to learn on the visual side.
Real time
Clock, scoreboard, "on air now", program progress: the plugin swaps only the items that changed, with no reload.
Your logic, your server
The plugin page is hosted by you. It can call your API, store data on the device and identify the device.
1. The plugin page
Create an HTML file (e.g. https://seusite.com/tevix/plugin.html) that loads the library and registers a handler. The page has no interface: it only answers the app.
<!DOCTYPE html>
<html><head><meta charset="utf-8">
<script src="https://tevix.app/js/tevix-plugin.js"></script>
</head><body><script>
TevixPlugin.setupHandler({
init: function (info) {
// info.app {name,color,logo}, info.code, info.lang ("pt"|"en"|"es"), info.device.id, info.tevix
},
ready: function () {
// connection ready: you can start timers here (e.g. update items every second)
},
handleRequest: function (id, data, reply) {
// the app asks for content. "init" is the default request when the plugin page opens.
if (id === "init") reply({
headline: "On air",
pages: [{ items: [
{ id: "agora", layout: "0,0,8,2", titleHeader: "Now", title: "Morning News", progress: 0.4, action: "video:plugin:aovivo" },
{ layout: "8,0,4,2", type: "button", icon: "list", label: "Schedule", action: "content:plugin:grade" }
]}]
});
else if (id === "aovivo") reply({ url: "https://cdn.seusite.com/live.m3u8", title: "Live" });
else if (id === "grade") reply({ headline: "Schedule", pages: [ /* ... */ ] });
else reply({ error: "unknown request: " + id });
},
handleData: function (data) {
// came from a "plugin:commit:SOMETHING" action (string)
},
handleEvent: function (name, data) {
// "player:play", "player:time" {position,duration}, "player:ended", "player:closed", "content:shown", "focus" {id}, "back", "visibility"
}
});
</script></body></html>
reply() accepts a page JSON (pages/template+items or blocks), an { "action": "video:..." } object to just run an action, or { "error": "message" }. It can also return a Promise. If the plugin does not answer within 20 seconds, the app shows an error with a Try again button.
2. Connecting the plugin to the app
In tevix.json, a menu item can point to the plugin instead of a page file:
{
"app": { "name": "City TV", "color": "#E8452C" },
"menu": [
{ "label": "Live", "icon": "live", "plugin": "https://seusite.com/tevix/plugin.html" },
{ "label": "News", "icon": "news", "content": "noticias.json" }
]
}
If the whole app is the plugin, use "plugin": "URL" directly at the root of tevix.json (no menu): the app opens without a sidebar. By default the app requests the init content; change it with "request": "other-id". Inside any page, static or coming from the plugin, use the actions in the table below. The plugin stays loaded while the app is open, even when switching pages; loading a plugin from another URL unloads the previous one.
| Action | What it does |
|---|---|
content:plugin:ID@URL | Loads the plugin from the URL (if not already loaded) and opens the ID content as a page, with Back. |
content:plugin:ID | Same, on the plugin already loaded. The ID cannot contain /, . or :. |
plugin:URL | Loads the plugin and opens the init content. Same as content:plugin:init@URL. |
plugin:commit:TEXT | Delivers TEXT to the plugin's handleData. Use it for buttons ("favorite", "sign in", "next"). |
video:plugin:ID · audio:plugin:ID | Asks the plugin for ID and plays whatever comes back: { "url", "title", "position" } or { "action" }. Ideal for signed or expiring URLs. |
panel:plugin:ID · panel:URL | Shows a panel (a window over the content) with a page JSON requested from the plugin or loaded from the URL. panel:close closes it. |
player:pause · resume · toggle · stop · seek:+30 · position:120 · label:TEXT | Controls the video/audio player in use and the text shown on it. |
focus:ID | Moves focus to the item with that id. |
plugin:reload · plugin:unload | Requests the current content again · unloads the plugin. |
[action1|action2] | Several actions in sequence. E.g. [panel:close|video:plugin:aovivo]. |
3. What the plugin can do in the app
All through the TevixPlugin library:
| Method | Effect |
|---|---|
updateItems([{ id, ...props }]) | Changes properties of on-screen items by id (title, image, progress, action...), without reloading the page. It is the heart of real time. |
updateContent(json) | Replaces the current page with another page JSON. |
showContent(json) | Opens a page JSON on top of the current one, with Back. |
reloadContent() | Makes the app request the current content again (calls handleRequest). |
showPanel(json) · closePanel() | Shows or closes a panel. |
play("video"|"audio", url, { title, position }) | Plays, optionally resuming from a position. |
setPlayerLabel(text) | Text on the player during playback ("Morning News · 12:04"). |
executeAction("...", data) | Any action from the table above (e.g. "player:pause", "back:", "content:plugin:grade"). |
updateMenu([...]) | Replaces the app menu (same format as the menu in tevix.json). |
requestData(id, cb) | Asks the app for data: "device:id", "info", "lang", "player:state", "content:current", "focus:current". |
requestDeviceId(cb) | Stable identifier of this device (for per-device login, limits, favorites). |
storage.set(key, value) · storage.get(key, cb) · storage.remove(key) | Stores data on the device, separated per plugin. |
busy(true, "Loading…") · busy(false) | "Please wait" indicator over the app. |
toast(text) · log() · warn() · error() | Quick on-screen notice · messages in the app console, for debugging. |
4. Events the app sends
They arrive in handleEvent(name, data):
| Event | When | Data |
|---|---|---|
player:play · player:pause · player:ended · player:error · player:loaded | Changes in the video or audio player. | { url, position, duration, paused, ended } |
player:time | Every second during video playback. | same |
player:closed | The user closed the video (Back). Save the position for "continue watching". | same |
content:shown | A page finished rendering. | { id, title, depth } |
focus | Focus reached an item with an id. | { id, panel } |
panel:shown · panel:closed | A panel opened or closed. | { id } |
back | The user pressed Back. | { depth, panel, overlay } |
visibility | The app went to the background or came back. | { hidden } |
5. Extra page and item properties
| Where | Property | Purpose |
|---|---|---|
| item | id | Identifies the item for updateItems, focus:ID and the focus event. |
| item | progress | Progress bar from 0 to 1 at the bottom of the item (live program, "continue watching"). |
| item | alignment | "left" (default), "center" or "right" for the texts. |
| item | imageFiller | "cover" (default), "contain" or "fill" for the image. |
| item | enabled: false | Dimmed item, not focusable. |
| item | color · background | Item colors (CSS). |
| page | ready | Action run as soon as the page appears (e.g. "plugin:commit:abriu"). |
| page | live | { "interval": 30, "action": "plugin:reload" }: repeats the action while the page is on screen. For finer updates, prefer updateItems. |
https://, so the plugin page must also be on https:// (a free Let's Encrypt certificate does the job). The videos and audio the plugin returns follow the same rule as the JSONs. The tevix-plugin.js library is small, has no dependencies and works in older TV browsers.tevix.json with the plugin, and open the code in the app. Open the browser console on your computer: the TevixPlugin.log() messages appear with the [plugin] prefix.Articles (text)
{
"k": "City",
"title": "City Hall announces new bus line",
"subtitle": "Service starts on Monday with 12 vehicles.",
"date": "November 2, 2026",
"author": "Newsroom",
"image": "https://seusite.com/img/onibus.jpg",
"body": [
"First paragraph.",
"Second paragraph."
]
}
Each entry in body becomes a paragraph. Plain text, no HTML. The reading view uses large type, comfortable from 3 meters away from the TV.
Persistent radio
With the player field in tevix.json, the app shows an always-visible radio bar. Listeners browse the news while they listen. Any item with audio: also plays in that bar. Use a direct stream (.mp3 or .aac), not a player page.
Menu icons
home live news video radio play movie tv music settings info link web star search list. Material Design icon names (play-circle-outline, live-tv, ondemand-video…) are converted automatically; unknown names become a dot.
Complete example to copy
The "Portal Cidade" demo app is published and can serve as a template. Download the files, replace the text, images and URLs, and publish them on your server:
- tevix.json (identity, menu and radio)
- inicio.json (hero, carousel and list), noticias.json (grid), radio.json (audio and
content:) - materia/1234.json (article)
To see it in the app: open TEVIX and enter 8CR-EEC.
Hosting and CORS
The JSON files live on your server, on any hosting that serves static files. The device fetches them directly from there, so two things are mandatory:
- Enable CORS: the server must respond with the
Access-Control-Allow-Origin: *header on the JSON files (and on images, if you wish). - HTTPS whenever possible. Free certificates (Let's Encrypt) do the job.
Nginx
location ~* \.json$ {
add_header Access-Control-Allow-Origin * always;
add_header Cache-Control "public, max-age=300";
}Apache (.htaccess)
<FilesMatch "\.json$">
Header set Access-Control-Allow-Origin "*"
Header set Cache-Control "public, max-age=300"
</FilesMatch>Cloudflare Pages, Netlify, Vercel, GitHub Pages
GitHub Pages already sends open CORS headers. On Netlify, create a _headers file with /*.json and Access-Control-Allow-Origin: *. On Cloudflare Pages and Vercel, use the platform's headers file with the same rule.
WordPress
Put the JSON files in a folder on your site (e.g. /tevix/) and apply the Apache rule above in that folder's .htaccess. Or generate the JSON files via a plugin/REST (see below).
Generate the files automatically
You don't need to write JSON by hand. Anything that produces a text file will do:
- WordPress site / RSS: a script (PHP, Python, Node) reads the RSS feed and writes
inicio.jsonandnoticias.jsonevery 5 minutes. Headline →title, summary →desc, featured image →image, article link → generate amateria/ID.jsonwith the text. - Radio: a fixed
radio.jsonwith the stream and the schedule; change the player'ssubtitleto the current show. - Videos: the YouTube API or your CDN lists the latest videos; use
video:with the direct MP4/HLS. YouTube pages do not play inside the app; the media file is required.
Test your app
- Open TEVIX in the browser (or on the TV) and enter your code. In the partner area there is a Test my app now button that signs in with your code automatically.
- Navigate with the keyboard arrows (or the remote) and OK/Enter. Back: Backspace or Esc.
- Changed a JSON file? Go to Settings → Clear cache and reload inside the app.
- The "Could not load this page" error shows the URL and the reason (404, CORS, invalid JSON). Validate your JSON with an online validator before publishing.
- In Settings → Check code now the app queries the TEVIX server immediately (useful right after renewing your plan).
Tips for looking good on TV
- 16:9, lightweight images (JPG up to 150 KB). No small text inside the image.
- Short titles. On TV, text is read from 3 meters away.
- Up to 8 menu items. Start with what your audience uses most (Live, Home).
- For live streams, use HLS (
.m3u8) with 2 or 3 quality levels. Progressive MP4 is fine for short videos. - If you use the Website type, make sure the site has visible focus and works with Tab/arrow keys; without a mouse, the user only has the remote.
Plans, trial and expiration
Free trial
When you create your code you get trial days (the deadline is shown in the partner area). Everything works just like the paid plan; the app shows a "Trial" badge.
Monthly plan
A fixed amount per month per code, with no device limit. Once renewed, the badge disappears and the deadline moves forward 30 days.
Expiration
Without renewal, the code is suspended. Devices show "Code suspended. Contact the company" on the next check (within 6 h). Once renewed, it starts working again on its own.
To subscribe or renew, use the contact shown in the partner area. The code always stays the same: your audience doesn't need to enter anything again.
Partner area
At /parceiro you sign in with your code and the password you chose at sign-up. There you can:
- See your plan status, its validity and how many devices used the app in the last 7 days.
- Change the content URL, name, color and logo. Devices update the next time they open.
- Copy ready-made instructions to share with your audience.
- Test the app with one click and change your password.
Frequently asked questions
Do I need to publish anything in the Samsung, LG or Google stores?
No. TEVIX is already (or will be) in the stores. Your audience installs TEVIX and enters your code.
How many devices can use the same code?
Unlimited. The code identifies your company, not a device.
Can I change the content later?
Yes, at any time, by changing your JSON files or the URL in the partner area. The code stays the same.
Does TEVIX host my videos or my radio?
No. Everything stays on your server or CDN. That keeps the service affordable and your content under your control.
I already have a start.json from another application. Do I need to redo it?
No. Register the URL of your start.json as a "Compatible format" source. Plugins from that format are not yet supported.
Which languages is the app available in?
Portuguese, English and Spanish. TEVIX detects the device language and lets you switch it in Settings. Your content appears in the language you write it in.
How does a user leave my app to enter another code?
In the app, Settings → Change code. With Website-type sources, press Back outside the site to open the settings.
What if I forget my partner area password?
Contact support through the contact in the partner area and provide your code; the password will be reset.
Create your code now and test it on every device.