> ## Documentation Index
> Fetch the complete documentation index at: https://docs.trysoar.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Connection & Authentication

> Open a socket, and authenticate with an API key to receive your own activity.

## Connect

Open a WebSocket to the gateway. Nothing is required at the handshake — no
headers, no query parameters, no subprotocol.

```
wss://ws.trysoar.com
```

| Environment | Host |
| - | - |
| Production | `wss://ws.trysoar.com` |
| Staging | `wss://ws.staging.trysoar.com` |

Once open, the socket is immediately usable for
[market data](/websockets/subscriptions). Authentication is only needed for
your own account's activity.

<Note>
  Market data needs no credential. If all you want is trades, order books,
  tickers or implied values, skip this page and go straight to
  [Subscriptions](/websockets/subscriptions).
</Note>

## Authenticate

To receive your own [fills](/websockets/user-fills),
[orders](/websockets/user-orders) and [positions](/websockets/market-positions),
send an `authenticate` frame carrying an API key.

```json theme={null}
{"type":"authenticate","data":{"token":"soar_sk_..."}}
```

On success the server replies with a `success` frame, and you are subscribed to
your own event stream. You do not subscribe to it explicitly — authenticating
is what enrolls you.

```json theme={null}
{"type":"success","message":"Authenticated successfully"}
```

Your private stream arrives as `prediction-market-update` frames. These are
independent of any `subscribe` you have sent; authenticating does not disturb
your market-data subscriptions, and subscribing does not disturb your private
stream.

<Note>
  A failed `authenticate` leaves the socket **open** for anonymous market data.
  You receive an [`error` frame](/websockets/errors-and-limits) and stay
  connected, unauthenticated — there is no need to reconnect before retrying
  with a working credential.
</Note>

### API keys

Issue an API key from your SOAR account settings. Keys are prefixed `soar_sk_`
and are shown once at creation — store the value then, because it is not
retrievable afterwards.

| Scope | Grants |
| - | - |
| `read` | Read access, including the WebSocket private stream |
| `trade` | Placing, amending and cancelling orders |

**Scopes do not nest.** A `trade`-only key is rejected on the WebSocket with
`forbidden_scope`, exactly as it is on the REST portfolio endpoints. A key
needs `read` specifically.

<Warning>
  An API key does **not** grant your account's notification feed. Notifications
  are first-party application traffic and `read` has never covered them.
</Warning>

### Replacing a credential

Re-authenticating resolves the new credential **first**, then replaces the old
one — so your private stream stays up for the length of the lookup.

If the new credential fails, you are left unauthenticated rather than silently
kept on the old one. Treat a failed re-authentication as "log in again", not as
"still logged in as before".

Only one `authenticate` may be in flight at a time. A second frame sent while
one is still resolving is rejected with `authenticate_in_progress`; wait for the
pending response and retry.

## Revocation

Revoking a key stops the private stream within **60 seconds**, matching the
REST promise. The server sends an `error` frame and then closes the socket:

| Situation | `code` |
| - | - |
| Key revoked or deleted | `unauthenticated` |
| Key no longer carries `read` | `forbidden_scope` |

Because the socket closes, your market-data subscriptions end along with the
private stream. Reconnect and resubscribe. Market data itself still needs no
credential, so an anonymous socket is enough to keep prices flowing while you
sort out the key.

## Connection limits

Concurrent authenticated connections are capped at **5 per API key**, counted
across all servers.

Excess `authenticate` attempts are refused with an `error` frame carrying code
`connection_limit`. The socket **stays open** and market data keeps flowing, so
you do not need to reconnect — close one of your other authenticated sockets
and try again.

<Note>
  The limit is enforced best-effort rather than transactionally, so sockets
  opening simultaneously on one key may briefly exceed it. Treat it as
  back-pressure, not a guarantee, and do not design around the exact number.
</Note>

## Replacing REST polling

The private stream is a drop-in replacement for polling `GET /v1/portfolio/fills`
and `GET /v1/orders/{order_id}`.

Start buffering incoming socket events, authenticate, and wait for the `success`
frame before loading history and current state over REST. Reconcile that response
with the buffered events, deduplicating fills by `tradeId`, then keep applying live
updates.

There is no atomic boundary between the REST response and the stream, and missed
events are not replayed. Loading REST history before authenticating can leave a
gap. Reconcile against REST after every reconnect.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.