← Back to API docs

Docs

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

  1. Your server mints a subscriber token for the channels a client may use (POST /tokens).
  2. The client opens a WebSocket with that token and receives messages (GET /subscribe).
  3. 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}/….

Channel API endpoints
Method & pathAuthPurpose
POST /v1/channel/{instance}/tokensAPI key (read; write for publish tokens)Mint a subscriber token.
POST /v1/channel/{instance}/publishAPI key (write) or a publish tokenFan a message out to a channel.
GET /v1/channel/{instance}/subscribeSubscriber tokenOpen a WebSocket to receive messages.

Channel requests are rate-limited per instance, with separate budgets so one kind of load cannot starve another and one busy app never throttles another. HTTP publishes get a sustained 300 per second (18,000 per minute) by default; token mints and new subscribe connections share a 200 per second (12,000 per minute) budget with a large burst to absorb reconnect storms. (WebSocket publishes are governed by their own per-connection cap instead.) Both are separate from the Search API's budget. You can set a lower limit per instance for each of these two budgets in the dashboard (Channels → your app → Rate limit); a configured value can only reduce the rate below the plan maximum, never raise it. Fan-out to subscribers is not rate-limited — only the publish call is — so a single publish reaching thousands of subscribers counts once. Over a limit, a request returns 429 with a RATE_LIMITED code; back off and retry.

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 subscribe-only token needs a read grant; minting a publish-capable token requires a write grant. The publish field controls which transport(s) the token may publish over:

publish field values
publishToken may publish
false (or omitted)Not at all — subscribe only.
"http"Over POST /publish only. Best for server-side publishing.
"ws"Over its WebSocket only (see Publish over WebSocket). Best for browser clients — it is rate-capped per connection.
"all"Both transports.
trueAlias for "all".
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 ws_url — the WebSocket URL with the token pre-filled (it carries both subscribe and, for a publish-capable token, publish):

{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.…",
  "expires_at": 1752345600,
  "channels": ["room-42"],
  "publish": false,
  "ws_url": "wss://api.altengine.net/v1/channel/live/subscribe?token=eyJhbG…"
}

Publish over HTTP

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 of "http" or "all" for that channel. The response reports how many open subscribers received it. (Browser clients can also publish over their socket — see Publish over WebSocket.)

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 }

Delivery is at-most-once: a message reaches only the subscribers connected at publish time — there is no backlog or replay for clients that are offline or reconnecting. Publishing to a channel with no open connections returns { "delivered": 0 }.

Subscribe over WebSocket

Open a WebSocket to the ws_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>. Requesting /subscribe without a WebSocket upgrade returns HTTP 426.

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:

WebSocket control frames
SendEffect
{ "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 — with WebSocket close code 4401 — so refresh the token and reconnect before then.

Publish over WebSocket

A token minted with publish of "ws" or "all" can publish up its own socket instead of calling POST /publish — lower latency for the sender (no per-message HTTP round-trip) and, because it rides an established connection, rate-capped per connection rather than against the instance's publish budget. Send a publish frame naming a channel the token is authorized for:

ws.send(JSON.stringify({ type: "publish", channel: "room-42", data: { text: "hello" } }));

The message fans out to that channel's subscribers as the same frame an HTTP publish produces ({ "channel", "data", "ts" }), and the server acknowledges to the sender with the delivery count:

{ "type": "published", "channel": "room-42", "delivered": 3 }

If the frame is rejected — the token lacks WS publish, the channel is not authorized, the message is over 32 KiB, or the per-connection rate cap (about 10 publishes per second) is exceeded — the server replies with an error frame instead, and the message is not delivered:

{ "type": "error", "error": "publish rate exceeded — slow down" }

WebSocket publish is ephemeral. Like all channel delivery it is at-most-once and never touches your backend, so there is no server-side record of the message. If you need message history, moderation, or validation, publish server-side instead: have the client send to your backend, which records the message and then calls HTTP publish. Reserve WebSocket publish for low-latency ephemeral traffic — chat where history is optional, typing indicators, cursors, and live reactions.

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.ws_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

Channel API limits
LimitValue
Channels per token100
Subscribers per channel10,000
Channel name length200 bytes
Message size32 KiB
WebSocket publishes per connection~10 per second
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.