---
title: WebSocket API
description: Call the platform API over a persistent WebSocket connection, and
  receive live notifications when agent instances change.
editUrl: true
head: []
template: doc
sidebar:
  hidden: false
  attrs: {}
pagefind: true
draft: false
---

The WebSocket API gives you a persistent connection to the Prefactor platform API. The same operations as the [HTTP API](/api/http) are available over the socket, and the socket can also receive server-pushed notifications when agent instances change. This page lists the connection details, message format, methods, and error and close codes; [Stream agent instance updates](/api/stream-instance-updates) walks through a working subscription.

The TypeScript and Python SDKs speak HTTP, not WebSocket — this API is for when you're writing your own client.

## Connecting

The endpoint is `wss://app.prefactorai.com/api/v1/ws`. Authenticate by sending an `Authorization: Bearer <token>` header on the upgrade request, using an [API token](/platform/concepts/api-token) — create one from the [Account › API tokens tab](/admin-ui/account/api-tokens). Both account tokens and deployment tokens work; the token's scope decides what you can call and subscribe to.

The browser `WebSocket` API cannot set request headers, so browser code cannot authenticate directly. Use a client that can, such as the `ws` package in Node.js, or put a proxy in front that adds the header.

A failed upgrade returns a plain HTTP response instead of opening the socket:

| Status | Cause |
| --- | --- |
| `400` | Not a WebSocket upgrade request, or a malformed `Authorization` header. |
| `401` | Missing, invalid, or expired token. |

## Message format

The socket speaks JSON-RPC 2.0 over text frames. Each request carries an `id`, which the response echoes back:

```json
{"jsonrpc": "2.0", "id": 1, "method": "agents/list", "params": {}}
```

```json
{"jsonrpc": "2.0", "id": 1, "result": {"status": "success", "summaries": []}}
```

Prefactor also sends notifications, which have no `id` — see [Notifications](#notifications):

```json
{"jsonrpc": "2.0", "method": "agent_instances/updated", "params": {"status": "success", "details": {"id": "01J..."}}}
```

Three rules to know:

- Send text frames only. Binary frames are rejected with an `invalid_request` error.
- JSON-RPC batch requests are not supported.
- A message without an `id` is treated as a notification and ignored — no response, no effect.

## Methods

Method names follow the resources: `agents/list`, `agent_instances/register`, `risk_profiles/show`, and so on. Params and results match the corresponding HTTP operations in the [OpenAPI spec](/api/platform); the [OpenRPC spec](#openrpc-spec) lists every method name with its schema. Two methods are worth knowing on their own:

| Method | Description |
| --- | --- |
| `bulk/execute` | Runs several operations in one request. Params and result match [POST /bulk](/api/platform/operations/bulkexecute). |
| `ping` | Returns details of the token you're authenticated with. Also useful as a keepalive — see [Connection lifecycle](#connection-lifecycle). |

One method is only available over WebSocket:

| Method | Params | Result |
| --- | --- | --- |
| `agent_instances/subscribe` | `agent_instance_id` (required), `idempotency_key` (optional) | `refs` — the records the connection is now subscribed to. |

## Notifications

After a successful `agent_instances/subscribe`, Prefactor pushes a notification each time the instance changes. Notifications are JSON-RPC messages with no `id`:

| Method | Sent when | Params |
| --- | --- | --- |
| `agent_instances/inserted` | The instance is created. | `details` — the full instance, in the same shape as [GET /agent_instance/{agent_instance_id}](/api/platform/operations/queryagentinstancegetdetailsbyid). |
| `agent_instances/updated` | The instance changes — status, span counts, and so on. | `details`, as above. |
| `agent_instances/deleted` | The instance is deleted. | `id` of the deleted instance; the record itself no longer exists. |
| `error` | Your token stops being valid. | `code` and `message` describing the failure. Prefactor closes the connection right after sending this — see [Connection lifecycle](#connection-lifecycle). |

Notifications are filtered by the token's permissions: if the token cannot see the instance, the notification is dropped rather than sent. Pushed notifications are not rate limited.

## Errors

Errors come back as JSON-RPC error objects. The top-level `code` is a JSON-RPC code; `data` carries the Prefactor error, in the same shape as an HTTP error response:

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Agent instance not found",
    "data": {"status": "error", "code": "not_found", "message": "Agent instance not found"}
  }
}
```

| Code | Meaning |
| --- | --- |
| `-32700` | Parse error — the frame was not valid JSON. The connection is then closed with code `1007`. |
| `-32600` | Invalid request — malformed JSON-RPC, a binary frame, or a batch request. |
| `-32601` | The operation is not implemented. |
| `-32602` | Invalid params, an unknown method name, or an application error such as `not_found`. `data.code` names the specific error. |
| `-32603` | Internal server error. |
| `-32000` | Rate limited. `data.retry_after_ms` says how long to wait before retrying — see [Rate limits](/api/rate-limits). |
| `-32001` | Temporarily unavailable. |

## Connection lifecycle

Prefactor does not send heartbeats. If you need to detect a dead connection, call `ping` on your own schedule.

| Close code | Meaning | What to do |
| --- | --- | --- |
| `1000` | Normal close. | Reconnect if you still need the stream. |
| `1007` | A frame contained malformed JSON. | Fix the payload, then reconnect. |
| `1008` | Authentication is no longer valid — the token was revoked, suspended, or expired, or the account, environment, or deployment it belongs to was deleted. Prefactor sends an `error` notification before closing. | Re-authenticate with a valid token before reconnecting. |

Nothing persists across connections. To resume after a reconnect, open a new socket and send `agent_instances/subscribe` again.

Calls over the socket count against the same rate limits as HTTP requests — [Rate limits](/api/rate-limits) covers how they are applied.

## OpenRPC spec

The machine-readable spec for the WebSocket API is at [https://app.prefactorai.com/api/v1/openrpc](https://app.prefactorai.com/api/v1/openrpc). It lists every JSON-RPC method with its params and result schemas, including the WebSocket-only subscription and the server-pushed notifications.

## Related

- [API Reference](/api) — the section overview: transports, rate limits, and the operation reference.
- [HTTP API](/api/http) — base URL, authentication, and error shapes for plain HTTPS calls.
- [Rate limits](/api/rate-limits) — how limits are applied, and the shape of a limited response.
- [API token](/platform/concepts/api-token) — account tokens and deployment tokens, and where to manage them.
- [Instance](/platform/concepts/instance) — what an agent instance is.
- [Stream agent instance updates](/api/stream-instance-updates) — subscribe to an instance and handle the notifications.