Skip to main content

Pick the right client for the runtime

ApolloMessagingClient

Publishable key. Safe in browsers and mobile webviews — that’s what makes the key “publishable”. Use it for anything an end user drives.

ApolloManagementClient

Organization API key. Server-side only — backend services, CI, cron. Never ship this key in client code.
If your publishable key is used from a browser, restrict it to your web origins and IP ranges in the Console so it can’t be replayed elsewhere.

Reuse one client instance

The messaging client caches its access token and refreshes it before expiry. Constructing a new client per request throws that away and forces a token exchange every time:

Error handling

All failures throw typed errors. ApolloError and ApolloTimeoutError are exported at the top level; per-status classes live under the Apollo namespace (BadRequestError, UnauthorizedError, ForbiddenError, NotFoundError, ConflictError, UnprocessableEntityError, InternalServerError, BadGatewayError).
error.body carries the API’s error envelope — branch on the stable error.code, and log error.request_id for support.

Timeouts and retries

There is no client-wide timeout — set one per call where it matters, and tune retries (default: 2) for the operation’s cost:
sendMessage triggers a billable agent turn. Automatic retries re-send the request — for user-driven sends prefer maxRetries: 0 and surface the error, rather than risking duplicate turns.

Filter your thread lists

An empty filters object sorts every thread in the organization and can be slow. Scope by project_id, agent_id, or a created range, and paginate with the returned cursors:

WebSocket resilience

  • Track seq and resume — keep the highest seq processed; on reconnect send { type: 'resume', resume_after: lastSeq } instead of re-submitting the turn (see WebSocket).
  • One handler per eventsocket.on() replaces the previous handler for the same event; route inside one handler rather than registering several.
  • Persist thread_id — keep it across reconnects (and page reloads, if the conversation should survive them) to stay in the same conversation.
  • Prefer REST for one-shot turns — if you don’t need live tokens or bidirectional traffic, sendMessage/streamMessage are simpler to operate.

Troubleshooting

The credential doesn’t match the client: messaging needs a publishable key (pk_network_...), management needs the organization API key. If the key has origin/IP restrictions, confirm the calling origin is allowlisted.
1008 is an authentication failure on the upgrade — the key exchange failed or the token was rejected. Verify the publishable key; 1011 signals a server-side error instead.
Unlike sendMessage, the stream request wraps the payload: streamMessage({ body: { user_id, text } }) — the top level also accepts the Last-Event-ID resume header.
Add filters (project_id, created range) and raise the per-call timeout: listThreads({ filters }, { timeoutInSeconds: 120 }).

Migrating from v2 (ia-controller)

v3 is a full rewrite against the Apollo API; the v2 surface is gone. The short version: every task_id becomes a thread_id, every client.controllerApi.X moves onto one of the two new clients, and the agent is now encoded in your key instead of passed per call.

Migration guide (v2 → v3)

The exact before/after for every call and response — method map, signature changes, WebSocket rewrite, response shapes, and a step-by-step checklist.