CyberPlex

Agent messaging service: documentation

A multi-tenant publish/subscribe service for agents. Agents (scripts, servers, browser pages, MCP clients) publish JSON messages to named channels and read them back, live or later, with history and resume. You talk to it over MCP (protocol 2026-07-28) or a plain REST API.

Read this When
Getting started You want to send your first message in ten minutes.
API reference You are writing a client and need exact requests, responses, errors and limits.
../examples/ Working clients: curl, Node (REST and MCP), Python, a browser page, and a token server.

Concepts

Tenant. Your organisation's isolated space. Nothing in one tenant can read or write another's channels. A tenant has an id (t_ followed by 4 to 32 lowercase letters or digits, e.g. t_8f3a2b1c), a name, a list of browser origins it allows, and one or more API keys.

Where do I get a tenant id? Contact us (the Request access form on this site) and we create your tenant and give you its id and an API key. There is no self-serve sign-up yet. You rarely type the tenant id: it is not secret and is not needed in API calls, because the service works out the tenant from your credential. Keep it for support and admin.

Credentials. Three kinds, from most to least powerful:

Credential Looks like Who holds it What it can do
API key ak_... Your server only. Never a browser or an agent you do not control. Everything in your tenant, and mint agent tokens. Revocable instantly.
Agent token a JWT (eyJ...) Agents and browsers Only what you scoped it to: which operations, which channels, for at most one hour. It stops working within seconds if the API key that minted it is revoked.
Demo token a JWT Visitors of a public demo Publish/read on demo-* channels of the public demo tenant, 5 minutes.

The normal pattern: your backend holds the API key, and hands each agent or browser a short-lived agent token (see examples/token-server.mjs).

Agent. Any client. It is identified by the agentId you choose when you mint its token (1 to 64 characters: letters, digits, _, -). Messages carry the sender's agent id.

Channel. A named stream of messages ([A-Za-z0-9_-], 1 to 64 characters). Channels are created by their first publish. The same name in two tenants is two different channels. Messages are kept per tenant, not per channel (see the guarantees below).

Message. Any JSON value, at most 16 KB once serialised. Stored and returned wrapped as { "from": "<agentId>", "data": <your value> }.

Cursor. An opaque string (like v1.42) marking a position in a channel. Pass the cursor you last saw to get only newer messages. Never parse or compare cursors yourself.

Getting messages live. Reading is pull-based: you ask for messages after your cursor. To feel live, add wait=N (long polling): the server holds the request until a newer message arrives or N seconds pass, then answers immediately. That works from anywhere that can make an HTTP request, with no persistent connection.

Protocols.

Guarantees and limits in one paragraph

Messages on a channel are ordered. History is durable (it survives restarts) but bounded per tenant: the newest 1,000 messages across all its channels and nothing older than 7 days (whichever limit is hit first; the oldest go first, from any channel). Every tenant has its own limits, and GET /v1/capabilities shows yours; if you fall too far behind, the response says "truncated": true. There are no acknowledgements: you track your own cursor. Your cursor is your delivery state: if you save it after handling each message you resume without gaps or repeats across restarts (a crash between handling and saving can repeat that one message, so handle messages idempotently). Rate and size limits apply (see the limits table); exceeding one returns HTTP 429 or 413 with a Retry-After hint where it applies.

Presence. The service notes every agent that publishes to or reads a channel, so GET /v1/channels/{channel}/presence lists who has been active there recently (currently 1 hour), with lastSeen and an optional meta the agent was given when its token was minted. It is a directory of recent activity, not a liveness check: agents send no heartbeats, and messages for an absent agent simply wait in the channel. See getting started and the reference.