Channel API
The Channel API is realtime pub/sub in the App Engine Channel tradition, modernized to WebSockets. Servers publish messages over HTTP; clients subscribe over a single WebSocket that can carry many channels. Clients authenticate with short-lived, channel-scoped tokens, so a browser subscribes without ever holding your API key. A channel with no open connections costs nothing.
How it fits together
- Your server mints a subscriber token for the channels a client may use (
POST /tokens). - The client opens a WebSocket with that token and receives messages (
GET /subscribe). - Your server (or a publish-capable client) publishes a message, which fans out to every open subscriber (
POST /publish).
Authentication and base URL
Send requests to https://api.altengine.net. The /tokens and /publish endpoints authenticate with an organization API key (bearer token); /subscribe authenticates with a subscriber token instead. Every path is scoped to a channel instance you provision in the console: /v1/channel/{instance}/….
| Method & path | Auth | Purpose |
|---|---|---|
POST /v1/channel/{instance}/tokens | API key (read; write for publish tokens) | Mint a subscriber token. |
POST /v1/channel/{instance}/publish | API key (write) or a publish token | Fan a message out to a channel. |
GET /v1/channel/{instance}/subscribe | Subscriber token | Open a WebSocket to receive messages. |
Mint a subscriber token
Mint a token server-side and hand it to your client. A token declares the channels it may use and expires after ttl_seconds (default 3600, max 14400). Minting a read-only subscriber token needs a read grant; setting publish: true mints a publish-capable token and requires a write grant.
curl -X POST https://api.altengine.net/v1/channel/live/tokens \
-H "Authorization: Bearer $ALTENGINE_KEY" \
-H "Content-Type: application/json" \
-d '{ "channels": ["room-42"], "ttl_seconds": 3600, "publish": false }'The response includes a ready-to-use subscribe_url with the token pre-filled:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.…",
"expires_at": 1752345600,
"channels": ["room-42"],
"publish": false,
"subscribe_url": "wss://api.altengine.net/v1/channel/live/subscribe?token=eyJhbG…"
}Publish
Publish a JSON data payload to one channel. Authenticate with an API key that has a write grant, or with a subscriber token minted with publish: true for that channel. The response reports how many open subscribers received it.
curl -X POST https://api.altengine.net/v1/channel/live/publish \
-H "Authorization: Bearer $ALTENGINE_KEY" \
-H "Content-Type: application/json" \
-d '{ "channel": "room-42", "data": { "text": "hello" } }'
# → { "delivered": 3 }Each subscriber receives the message as a JSON frame carrying the channel, your payload, and a server timestamp (milliseconds since the epoch):
{ "channel": "room-42", "data": { "text": "hello" }, "ts": 1752345600000 }Subscribe over WebSocket
Open a WebSocket to the subscribe_url (or build it yourself: wss://api.altengine.net/v1/channel/{instance}/subscribe?token=…). The connection subscribes to all of the token's channels by default; pass ?channel= (repeatable) or ?channels=a,b to start with a subset. Browsers that cannot set query strings may instead pass the token as a subprotocol: Sec-WebSocket-Protocol: bearer, <token>.
One socket carries many channels. Incoming frames are the message objects shown above. You can adjust the live subscription — within the channels the token authorized — by sending control frames:
| Send | Effect |
|---|---|
{ "type": "subscribe", "channels": ["room-7"] } | Add channels to this connection. |
{ "type": "unsubscribe", "channels": ["room-7"] } | Remove channels from this connection. |
ping (raw string) | Keepalive; the server replies pong. |
After a subscribe or unsubscribe, the server acknowledges with the connection's current set: { "type": "subscribed", "channels": [ … ] }. The socket is closed automatically when the token expires, so refresh the token and reconnect before then.
End-to-end example
// Server: mint a token (never expose the API key to the browser)
const res = await fetch("https://api.altengine.net/v1/channel/live/tokens", {
method: "POST",
headers: {
"Authorization": "Bearer " + process.env.ALTENGINE_KEY,
"Content-Type": "application/json"
},
body: JSON.stringify({ channels: ["room-42"], ttl_seconds: 3600 })
});
const data = await res.json();
// Browser: connect with the returned URL
const ws = new WebSocket(data.subscribe_url);
ws.onmessage = (e) => {
const msg = JSON.parse(e.data);
console.log(msg.channel, msg.data, msg.ts);
};
// Adjust channels live (within the token's authorized set)
ws.send(JSON.stringify({ type: "subscribe", channels: ["room-7"] }));Limits
| Limit | Value |
|---|---|
| Channels per token | 100 |
| Channel name length | 200 bytes |
| Message size | 32 KiB |
| Token TTL (default) | 3600 seconds (1 hour) |
| Token TTL (max) | 14400 seconds (4 hours) |
Channel names must be printable ASCII, may not start with !, and may not use the reserved __*__ form.