> For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/swap-api/reference/direct/connection.md).

# Connection & Negotiation

Connect to Titan Direct, negotiate the protocol version and compression, and send your first request.

**Titan Direct is a persistent WebSocket connection.** All messages are binary frames encoded with [MessagePack](/titan/developer-doc/swap-api/reference/wire-protocol.md). Before the first request, the client and server negotiate a protocol version and optional compression scheme via the `Sec-WebSocket-Protocol` header.

## Protocol negotiation

The client lists supported protocols in order of preference via the `Sec-WebSocket-Protocol` header. The server selects the best mutual match and confirms it in the response header. All version 1 protocols begin with `v1.api.titan.ag`, optionally suffixed with `+zstd`, `+brotli`, or `+gzip` for compression. Without a suffix, no compression is used.

**Compression is applied after MessagePack encoding and must be reversed before decoding on the receiving end.** All Titan SDK examples and guides use zstd.

## Authentication

**The server requires authentication before any requests can be made.** Credentials are submitted via a signed **JWT (JSON Web Token)** when opening the connection:

* **Authorization header** (recommended) — `Authorization: Bearer <token>`
* **Query parameter** — `wss://YOUR_ENDPOINT/api/v1/ws?auth=<token>` — for clients that cannot set custom headers (e.g. browsers)

### JWT claims

**Required:**

* **`iss`** — Issuer of the JWT.
* **`sub`** — Subject, a unique identifier for the authenticated user.
* **`aud`** — Audience, **must be `api.titan.ag`**.
* **`exp`** — Expiration time. Connections are refused if this time is in the past.
* **`iat`** — Issued-at time. Tokens with issue times in the future are rejected.

**Optional:**

* **`nbf`** — Not-before time. Connections are refused if this time is in the future.
* **`jti`** — JWT ID. If supported by the server, only one connection per unique `jti` value is accepted.
* **`https://api.titan.ag/upk_b58`** — A Solana public key as a Base58 string. If set, this key is used for transaction generation and **any attempt to submit a different `userPublicKey` will result in an error**.

## Connecting

```typescript
import WebSocket from 'ws';
import { Encoder, Decoder } from '@msgpack/msgpack';
import { zstdCompress, zstdDecompress } from 'http-encoding';

// useBigInt64 is required — u64 values (amounts, timestamps) exceed Number.MAX_SAFE_INTEGER
const encoder = new Encoder({ useBigInt64: true });
const decoder = new Decoder({ useBigInt64: true });
let useCompression = false;

// Pass your API key as a query parameter
const url = `${process.env.TITAN_ENDPOINT}?auth=${process.env.TITAN_API_KEY}`;

// Offer zstd compression with a plaintext fallback
const ws = new WebSocket(url, [
  'v1.api.titan.ag+zstd',
  'v1.api.titan.ag',
]);

ws.on('open', () => {
  // Check which protocol the server selected
  useCompression = ws.protocol !== 'v1.api.titan.ag';
  console.log('Connected. Negotiated protocol:', ws.protocol);
});
```

## Message format

**All messages are binary WebSocket frames — text frames are ignored.** Every message is MessagePack encoded. For encoding conventions, data types, and serialization details, see [Wire Protocol](/titan/developer-doc/swap-api/reference/wire-protocol.md).

## RPC interface

The client interacts with the server by making requests with a given set of parameters and receiving a response (success or error) for each request.

| Procedure            | Parameters             | Response                  | Stream       |
| -------------------- | ---------------------- | ------------------------- | ------------ |
| `GetInfo`            | `GetInfoRequest`       | `ServerInfo`              | —            |
| `NewSwapQuoteStream` | `SwapQuoteRequest`     | `QuoteSwapStreamResponse` | `SwapQuotes` |
| `StopStream`         | `StopStreamRequest`    | `StopStreamResponse`      | —            |
| `GetVenues`          | `GetVenuesRequest`     | `VenueInfo`               | —            |
| `ListProviders`      | `ListProvidersRequest` | `ProviderInfo[]`          | —            |

Every client request is a [`ClientRequest`](/titan/developer-doc/swap-api/reference/types.md#message-envelope-types) with a numeric `id` and a `data` field containing one of the RPC methods above. The server matches responses to requests via `requestId` and responds with one of four message types: **`Response`**, **`Error`**, **`StreamData`**, or **`StreamEnd`**.

See [Types Reference](/titan/developer-doc/swap-api/reference/types.md#message-envelope-types) for the full type definitions.

### Sending a request

```typescript
// Monotonically increasing counter — server echoes this back in responses
let requestId = 0;

// Encode and optionally compress before sending
async function sendRequest(ws: WebSocket, id: number, data: Record<string, unknown>) {
  const encoded = encoder.encode({ id, data });
  ws.send(useCompression ? await zstdCompress(encoded) : encoded);
}

// Decompress (if needed) and decode incoming messages
async function decodeMessage(raw: Buffer): Promise<any> {
  const data = useCompression ? await zstdDecompress(raw) : raw;
  return decoder.decode(data);
}

// Send GetInfo to confirm the connection is live
await sendRequest(ws, requestId++, { GetInfo: {} });

// Handle all four server message types
ws.on('message', async (raw: Buffer) => {
  const msg = await decodeMessage(raw);

  if ('Response' in msg) {
    // Successful RPC response — correlate via requestId
    console.log('Response to request', msg.Response.requestId, msg.Response.data);
  }

  if ('Error' in msg) {
    // RPC error — contains requestId, code, and message
    console.error('Error on request', msg.Error.requestId, msg.Error.code, msg.Error.message);
  }

  if ('StreamData' in msg) {
    // Stream update — contains id, seq, and payload
  }

  if ('StreamEnd' in msg) {
    // Stream closed — check errorCode/errorMessage for abnormal termination
  }
});
```

{% hint style="info" %}
Use a single incrementing `requestId` counter per connection. The server returns the same `requestId` in its response, so you can correlate requests and responses without additional bookkeeping.
{% endhint %}

## Ping / Pong

The client and server both support standard WebSocket Ping/Pong frames. The `ws` library handles these automatically — **no explicit handling needed** unless you want to monitor connection health manually.

***

## Related pages

* [GetInfo](/titan/developer-doc/swap-api/reference/direct/get-info.md) — confirm the server is reachable and read default settings
* [Wire Protocol](/titan/developer-doc/swap-api/reference/wire-protocol.md) — MessagePack encoding and serialization details
* [Titan Direct vs Titan Gateway](/titan/developer-doc/swap-api/reference/direct-vs-gateway.md) — choosing between WebSocket and REST
