Skip to main content
mountAgents (SDK ≥ 0.12.0) is the front door for an Edge Compute function that serves agents. One call replaces the routing file a function would otherwise write by hand. The function keeps only its own policy. It ships at @telnyx/edge-runtime/mount.
The entry must export an object carrying fetch, not the handler itself. mountAgents returns a fetch handler, so it goes on the fetch property of the module’s default export. A bare-function default export — export default mountAgents(map) — is refused by the function runtime at load time, and the failure is a bundle that never starts rather than a request that 500s.

The Address, and What Selects the Transport

Every mounted agent answers on the same shape, <base>/<mount>/<name>, where <mount> is a key of the map and <name> is the routing name of one instance. The request picks the transport; the path never changes: base defaults to /agents, so the map above serves /agents/conversation/alice on all three. That is the URL the AgentClient examples connect to — wss://my-func.telnyxcompute.com/agents/conversation/alice is this default base, this mount key, and this name:
A hand-written front door can serve any shape it likes; mountAgents is what makes this shape route. The upgrade is handed to the agent’s webSocket; GET and POST are handed to the agent’s fetch, where an AgentHttpServer over the agent’s connection engine answers them once the class routes fetch there. The forwarded request keeps its URL and query string, so subscribe, Last-Event-ID resume, and the /rpc/<method> segment all arrive as the caller wrote them.
SSE arrives in batches on deployed functions. Each SSE response carries the frames ready when the agent answers, and the EventSource reconnects about once a second. The WebSocket and RPC paths are unaffected, and SSE streams live in local development. AgentHttpServer has the detail. The WebSocket path is the live one for production, on the same address under the same policy.

The Hand-Off

The handler the mount returns resolves the binding, then hands the request to the instance.
  • fetch on the function’s default export is the entry this handler fills. The platform calls it with each request and the function’s env.
  • ActorNamespace is what each map value must be. map(env) reads the bindings on every request. The mount answers 404 for a value without idFromName.
  • idFromName receives the actor id that encodeAgentName built. The same id always reaches the same instance, as Addressing describes.
  • ActorStub.fetch receives the request with its URL unchanged and the stamped headers. An upgrade reaches the instance’s webSocket entry, and any other request reaches its fetch entry.

Two Gates, Not One

The mount’s authorize does not replace the agent’s own authorize(token, req). It composes with it, and both run:
  1. At the edge, before any actor wakes — authorize(req, route) sees the inbound request and what the mount resolved from it (mount, name, actorId, transport, and for RPC the method). Return a Response to reject — nothing is forwarded and no actor is woken — or return headers to stamp identity onto the request, or nothing to admit as-is. It runs identically on all three transports, so no request shape reaches an agent around it.
  2. Inside the agent — the caller’s credential rides through untouched (?token=, or an Authorization header), so Agent.authorize resolves this connection’s claims from it exactly as it would unmounted, and onAttach may still veto.
Stamp identity at the mount; decide grants in the agent.
A stamped header always wins: it overwrites a header of the same name the caller sent, so a client cannot forge an identity the agent trusts. Headers the mount does not stamp are forwarded as the caller sent them, so the agent trusts only what the mount stamps. The Mounting Agents guide walks the whole path, including composing the mount with other routes and the cross-origin opt-in.

Names

A routing name is what a caller writes in the address; an actor id is what the platform files an instance’s state and timers under, and only ASCII letters, digits, -, _, and . are addressable. mountAgents runs every name it resolves through encodeAgentName, so natural names — a phone number, an email address, a composite key, a name with accents or emoji — reach a persistent instance without the caller thinking about encoding. Any front door written by hand that must address the same instances calls the same function. Re-deriving the rule points a name at a different, empty instance. The instance reads its actor id as ctx.id. The ceiling is on the id once URL-escaped, not on the name — see MAX_ESCAPED_ACTOR_ID_LENGTH on the Limits page for what each kind of character costs (225 ASCII, 54 two-byte, 36 three-byte, 27 emoji, plus 6 per escaped run) and why an over-long id fails only after an eviction rather than on first use.