Recipe: build a live shopping channel
Sub-second WebRTC from a phone or webcam, live chat and floating reactions over the data channel, a viewer counter, a buy button, and the whole session recorded. The full compraventa en vivo build.
Why live shopping
Live selling collapses the distance between "I saw it" and "I bought it". A host holds up the product, answers questions in the chat, and the audience checks out without leaving the stream. The two things that make or break it are latency (a two-second delay ruins a Q&A) and interaction (chat, reactions, a live headcount). StreamHub gives you both from one app.
Architecture in words
The host publishes over WebRTC for sub-second glass-to-glass latency. Viewers subscribe with an anonymous play-token, so anyone with the link can watch. Chat and reactions ride LiveKit data channels (topics chat and reaction); the viewer counter reads real subscribers. A ready-made live-shopping.html sample bundles the low-latency player, the chat, the reactions and a buy button, and the session records to your S3 for replays.
1. Create the app and turn on the interactive features
Chat, reactions and the viewer counter are off by default — enable them first:
export BASE="https://your-server.example.com/api/v1"
export TOKEN="sk_...."
curl -s -X POST $BASE/apps -H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"name":"shop","displayName":"Live Shop"}'
curl -s -X PATCH $BASE/apps/shop -H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"features":{"chat":true,"reactions":true,"viewerCounter":true}}'
2. Give the host a publish token
The host goes live from a browser (webcam + mic) with a publish token:
curl -s -X POST $BASE/apps/shop/tokens -H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"room":"friday-drop","canPublish":true,"canSubscribe":true}'
# → data.token / data.wsUrl — connect and publish
The response includes token, wsUrl, and copyable playUrl / embedUrl / iframe.
3. Put the audience in front of the sample
The turnkey live-shopping.html sample is served publicly and already wires the WebRTC player, chat, reactions and a "Buy" button. It authenticates viewers with the public per-room play-token, so no login is needed. Point buyers straight at your checkout with the buyUrl query parameter:
https://your-server.example.com/samples/shop/live-shopping.html?buyUrl=https://tienda.example.com/checkout
Manage or regenerate the samples with GET /apps/shop/samples and POST /apps/shop/samples/regenerate.
4. Chat, reactions and the headcount
Normal viewer chat flows directly over the LiveKit data channel from the sample page — no API call. When you want the server to inject a message (a pinned "20% off for the next 10 minutes", a system notice, or to guarantee a callback fires), post it:
curl -s -X POST "$BASE/apps/shop/streams/friday-drop%2Fhost/data" \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"topic":"chat","message":"Envio gratis en la proxima hora","from":"Tienda"}'
The viewer count is on the stream detail — GET /apps/shop/streams/<id> returns data.viewers (real subscribers; the host and hidden monitors are excluded) whenever viewerCounter is on, and it is also exported as the Prometheus gauge streamhub_stream_viewers.
5. Record the session
Capture the whole show to your bucket for a replay or clips. For a single host, record just that publisher with ffmpeg (no headless Chrome):
curl -s -X POST $BASE/apps/shop/recording/start -H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"roomName":"shop-friday-drop","recordMode":"track"}'
When egress finishes, the VOD turns ready and vod_ready fires.
What you should see
The host is live with sub-second latency. On the sample page, chat messages and floating reactions appear instantly, the viewer number ticks up as people join, and the buy button opens your checkout. After the show, a VOD of the session sits in your S3.
Troubleshooting
- Chat or reactions do nothing — they are per-app features and off by default. Confirm the
PATCHin step 1 landed; areactionpost to a disabled app returns 404. - Latency feels like broadcast, not live — make sure viewers are on the WebRTC player (the sample), not an HLS URL. HLS trades latency for scale; for interactive selling you want WebRTC.
- No checkout — StreamHub ships no payments backend. The buy button is a launcher; wire
buyUrlto your own store or payment link.
Closing
Everything here runs on infrastructure you own, with no per-minute streaming bill eating your margin. If you want a branded storefront player, moderation tooling, or the checkout integration built around this flow, that is a natural consulting engagement on top of the open-source core.