Realtime Public Feed
Category: Backend & APIs
This page is generated from the Air Pipe marketplace. Browse it live to install into your organization.
The simplest way to add realtime to a website: a public WebSocket feed your visitors subscribe to with no login, no token, no API key. Your backend publishes; every open browser gets the update instantly. It's the Ably/Pusher "public channel" — for live scores, prices, status pages, countdowns, announcements, and activity tickers.
Use public feeds for non-sensitive data only. For per-user or private channels (auth, presence, history), use the Realtime WebSocket Channels pack, which adds the full auth ladder.
Why this is safe to open
public: true lets a browser connect with no credential — but the per-organisation realtime limits (publish rate, subscriptions per connection, distinct topics, retained messages) still apply, so an open feed can't be used to exhaust a shared node. Open access, bounded blast radius.
What's included
| File | Purpose |
|---|---|
feed.yml | A public feed interface (subscribe + receive, no auth) and a backend publish route. |
Endpoints
| Interface | Connect / call | Auth |
|---|---|---|
feed | wss://<host>/ws/<ORG_UUID>/production/feed | none (public) |
publish | POST https://<host>/publish | your backend (protect it) |
Quick start
Browser — subscribe + receive (no auth):
<script>
const ws = new WebSocket("wss://your-airpipe-host/ws/<ORG_UUID>/production/feed");
ws.onopen = () => ws.send(JSON.stringify({ ap_subscribe: "updates" }));
ws.onmessage = (e) => {
const m = JSON.parse(e.data);
if (m.ap_channel === "updates") console.log("update:", m.data);
};
</script>
Your backend — publish to everyone:
curl -sX POST https://your-airpipe-host/publish -H 'content-type: application/json' -d '{
"channels": ["updates"],
"data": { "message": "Store opens in 5 minutes!" }
}'
# → every browser subscribed to "updates" receives { "ap_channel":"updates", "data":{…} }
That's the whole thing — no keys in the page, no broker to run.
Control frames (client → server)
| Frame | Effect |
|---|---|
{"ap_subscribe": "updates"} / {"ap_subscribe": ["a","b"]} | Join channel(s); wildcards room:+, room:# work too |
{"ap_unsubscribe": "updates"} | Leave channel(s) |
Pushes arrive as {"ap_channel":"updates","data":{…}}.
Customisation
- Protect
/publish: add anis_valid_jwt/validate_keyassert so only your backend can broadcast (the feed stays public to read). - Publish from any pipeline: add a
ws_publishaction to an existing HTTP/DB/scheduled pipeline so, e.g., a new DB row also fans out to the feed. - Go private / add presence + history: move to the Realtime WebSocket Channels pack — same shape, plus open → Air Pipe-issued → shared-secret → JWKS auth, presence and history-rewind.
Notes
- Public feeds are for non-sensitive data — anyone can subscribe. Put anything private behind the authenticated pack.
- Cross-node fan-out uses your private node mesh — no Redis/NATS/broker to operate.
- Each delivered message counts as one request against your plan.
- Only
productionandstagingenvironments are accepted in the URL.
Configuration
feed.yml
name: RealtimePublicFeed
description: >
The simplest realtime — a PUBLIC WebSocket feed your website visitors subscribe to with NO auth at
all. Live scores, status, prices, announcements, activity. Your backend publishes; every open
browser gets it instantly. The Ably/Pusher "public channel", in one small config, no keys in the
browser and no message broker to run.
docs: true
# ── How it works ─────────────────────────────────────────────────────────────────
# 1. A visitor's browser opens a WebSocket to:
# wss://your-airpipe-host/ws/<ORG_UUID>/production/feed
# and subscribes with a JSON frame (NO token, NO API key):
# {"ap_subscribe": "updates"}
# then receives every push as:
# {"ap_channel": "updates", "data": { ... }}
#
# 2. Your backend publishes to the channel by POSTing to /publish. Every subscriber (on any node)
# gets it. `public: true` is what allows the credential-less browser connection — use it ONLY for
# NON-SENSITIVE data. Per-org limits (publish rate, subscriptions, topics) still bound it, so an
# open feed can't exhaust the node.
interfaces:
# The public feed. `public: true` = a browser may connect with no credential.
feed:
ws: "on" # realtime — no `output` needed
# output: http # optional: also serve this interface over HTTP (dual interface)
public: true
summary: Public realtime feed (no auth)
description: Connect with no credential, subscribe to a channel, and receive live updates.
tags: [websocket, realtime, public, pubsub]
actions:
- name: Ping
input: a|body|
# Backend publish surface. YOUR server POSTs here to push to every subscriber. The feed is public
# to READ, but you should protect THIS route (add an is_valid_jwt / validate_key assert) so only
# your backend can broadcast. Any pipeline can also push with a `ws_publish` action.
publish:
output: http
method: POST
summary: Publish to the public feed (backend)
description: Fan a payload out to everyone subscribed to the channel(s).
tags: [websocket, realtime, push]
request_example:
channels: ["updates"]
data:
message: "Store opens in 5 minutes!"
actions:
- name: Broadcast
ws_publish:
channels: a|body::channels|
data: a|body::data|