Partner TV App
A hotel can run another company's TV app on its in-room TVs without any code being installed on the TV. You give the console the app's web address; the TV opens that address full screen. The TV's own background service — power commands, reboot, room provisioning — keeps running behind it, so the TV stays managed.
This page has two parts: for hotel staff (how to deploy the address) and for the vendor (what the app must do).
For hotel staff
- Open Content → App Templates. It needs the Content area, Operator or above.
- In the template's TV app list, choose Partner app (URL).
- Type the address the vendor gave you — for example
https://tv.vendor.example/app— and press Deploy.
The address must start with https:// and must not contain a user name or
password. The console tells you what is wrong before you can deploy.
Every TV in a room that uses this template moves to the partner app within seconds, if it is on; TVs that are off move when they start. To go back, choose another TV app in the list — the address stays saved for next time.
Which rooms use which template is set in the same screen (Rooms), and a guest can get their own template at check-in — see App Templates.
If the address stops working (the vendor's server is down, or the app refuses to be embedded), the TV shows a black screen with Trying to reach the TV app… and tries again every half minute. Switch the template back to a built-in TV app to restore the standard screen while the vendor fixes it.
For the vendor
Your app is an ordinary web app — a Flutter web build, or any site. Nothing from us is added to it. Three things are required.
1. Allow embedding
The TV shows your app inside a frame on https://pcr-tv.pages.dev. Your server
must not send X-Frame-Options: DENY or SAMEORIGIN, and if it sends a
Content-Security-Policy header its frame-ancestors must include
https://pcr-tv.pages.dev.
The TV loads the frame with sandbox="allow-scripts allow-same-origin allow-forms allow-popups allow-modals allow-presentation". Your page cannot navigate the
top window — that keeps the TV's background service alive.
2. Size and keys
The stage is 1920 × 1080; your page fills it. Remote-control keys reach your page as ordinary keyboard events (arrow keys, Enter, Back) once the page has focus — the TV focuses your frame when it loads.
3. (Optional) Ask the TV about the room
The TV adds two parameters to your address:
| Parameter | Meaning |
|---|---|
pcr_room |
The room number, for example 633 |
pcr_hotel |
The hotel's id |
For anything more, use messages. Your page tells the TV it is ready; the TV answers with the room's details and sends them again whenever they change (a new guest, a new pairing code):
window.addEventListener('message', (e) => {
if (e.source !== window.parent) return // only trust the TV page
const m = e.data
if (m?.type !== 'pcr:context' || m.v !== 1) return
// m.hotel { id, name }
// m.room { id, number }
// m.guest { name, language } (null when nobody is checked in)
// m.pairing { code, qr } the 6-digit code and the QR address for the phone app
})
window.parent.postMessage({ type: 'pcr:hello', v: 1 }, '*')
The pairing code is what a guest types into the phone app to connect to the room. Show it (or the QR) on your screen so guests can control the room from their phone. The TV never sends its device secret or the room key.
A working example is at pcr-tv.pages.dev/partner-sample/.
Ask the TV: the internal API
Your page is a web page inside a frame, so it cannot reach the TV's own functions (changing the channel, opening
Netflix) and it does not hold any key of ours. The TV does both for you, over the same messages you already use. Send a
request, get an answer with the same id:
// you → TV
window.parent.postMessage({ type: 'pcr:request', v: 1, id: 7, method: 'channels' }, '*')
// TV → you (listen for it like pcr:context)
// { type: 'pcr:response', v: 1, id: 7, ok: true, data: [ … ] }
// { type: 'pcr:response', v: 1, id: 7, ok: false, error: { code: 'unavailable', message: '…' } }
method |
params |
Answer (data) |
|---|---|---|
stay |
— | { hotel, room, guest } — the same as the context; guest is null for an empty room |
channels |
— | The channels guests see, as in the Client API (id, number, name, kind, icon, encrypted, tune) |
apps |
— | The apps guests see, as in the Client API (id, title, icon) |
tune |
{ channelId } or { number } |
Changes the TV to that channel. { channelId, number } when done |
launch |
{ appId } |
Opens that app on the TV. { appId } when done |
Rules: tune and launch only work for channels and apps that are in the hotel's lists — you cannot make the TV
open anything else; a second tune/launch within half a second is refused (too_fast); the lists are cached for a
minute. Error codes: unknown_method, bad_params, not_found, unavailable (the list could not be read),
too_fast, tv_error (the TV refused — the message says why; a desktop browser has no TV to tune).
The sample at pcr-tv.pages.dev/partner-sample/ shows the apps and the
first channels and lets you press them.
Controlling the room's devices
To read or change lights, curtains and the thermostat from your app, use the Client API with a partner key from the hotel.
Test before the hotel deploys
Open your address inside any page on https:// to check it can be embedded: if
the frame stays blank and the browser console mentions frame-ancestors or
X-Frame-Options, fix the headers above. Then ask the hotel to deploy the address
to one room's template and check it on a real TV — remote-control keys and video
playback inside a frame depend on the TV's firmware.