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

# Post-Trade WebSocket

> Connect to the maker post-trade WebSocket to receive quote:accepted, quote:confirmed, and quote:rejected events. Protocol version 3.

The maker WebSocket is the **post-trade** channel (channel 3 of the [three-channel transport](/guides/market-maker)). It is a server → maker push connection that delivers win/loss notifications once a taker commits. It no longer carries quote requests or quote submissions — those moved to the [SSE quote-request stream](/api-reference/market-maker/quote-stream) (in) and [REST quote endpoints](/api-reference/market-maker/submit-quote) (out).

## Endpoint

```
wss://api.convallax.com/maker/v1/ws
```

## Authentication

Each market maker authenticates with a **per-MM API key** generated from the [dashboard](https://convallax.com/settings). The key maps to a stable `makerId`. Authenticate either way:

* **Query parameter:** `wss://api.convallax.com/maker/v1/ws?apiKey=<your-key>`
* **`auth` message:** send `{ "type": "auth", "apiKey": "<your-key>" }` immediately after the socket opens.

On successful authentication, the server sends a confirmation that echoes your resolved `makerId`:

```json theme={null}
{
  "type": "connected",
  "protocolVersion": 3,
  "makerId": "mm-alpha",
  "authenticated": true,
  "serverTime": "2026-06-15T12:00:00.000Z"
}
```

If the API key is missing or invalid (when the relay is configured with keys), the connection is closed with code `4001`. If the relay is started without any keys, it runs in **OPEN dev mode** (anonymous `makerId`, accepts anyone). See the [Authentication guide](/guides/authentication).

<Note>
  The post-trade WebSocket authenticates via `?apiKey=` or the `auth` message. The [SSE quote-request stream](/api-reference/market-maker/quote-stream) and [REST quote endpoints](/api-reference/market-maker/submit-quote) authenticate via the `X-API-Key` header.
</Note>

## Keep-Alive

The server sends a WebSocket **ping** every \~25 seconds; respond with a **pong** to maintain the connection (most libraries do this automatically). You may also send an application-level `{ "type": "ping" }`, to which the relay replies `{ "type": "pong", "timestamp": "..." }`.

## Server → Maker Messages

### `connected` — Authentication confirmation

<ResponseField name="type" type="string">
  Always `"connected"`.
</ResponseField>

<ResponseField name="protocolVersion" type="number">
  Current protocol version. Currently `3`.
</ResponseField>

<ResponseField name="makerId" type="string">
  Your resolved maker identifier.
</ResponseField>

<ResponseField name="authenticated" type="boolean">
  `true` once your API key is accepted.
</ResponseField>

<ResponseField name="serverTime" type="string">
  ISO 8601 server time at connection.
</ResponseField>

### `quote:accepted` — You won; sign and confirm

Sent **only to the winning maker** when a taker commits. Sign the `order` with the wallet key for `order.maker` (using the provided `domain` and `types`) and **POST the signature** to [`POST /v1/mm/quotes/:quoteId/confirm`](/api-reference/market-maker/confirm-quote) before `confirmationDeadline`. The backend verifies that your signature recovers to `order.maker`.

<ResponseField name="type" type="string">
  Always `"quote:accepted"`.
</ResponseField>

<ResponseField name="quoteId" type="string">
  The server-generated `quoteId` of your winning quote. Use it in the confirm endpoint path.
</ResponseField>

<ResponseField name="requestId" type="string">
  The quote request you won.
</ResponseField>

<ResponseField name="order" type="object">
  The EIP-712 `Order` struct to sign: `maker`, `seriesId`, `optionAmount`, `premiumAmount`, `makerSelling`, `taker`, `validUntil`, `nonce`.
</ResponseField>

<ResponseField name="domain" type="object">
  EIP-712 domain: `name` `"ConvallaxRFQSettlement"`, `version` `"1"`, `chainId` `80002`, `verifyingContract`.
</ResponseField>

<ResponseField name="types" type="object">
  EIP-712 type definitions for `Order`.
</ResponseField>

<ResponseField name="confirmationDeadline" type="string">
  ISO 8601 deadline to POST your signature (controlled by `MAKER_CONFIRMATION_MS`). Miss it and the backend falls back to the next-best maker.
</ResponseField>

```json theme={null}
{
  "type": "quote:accepted",
  "quoteId": "8f3c2b1a-...",
  "requestId": "req-789",
  "order": {
    "maker": "0xYourMakerWallet...",
    "seriesId": "98765432109876543210",
    "optionAmount": "10000000",
    "premiumAmount": "1200000",
    "makerSelling": true,
    "taker": "0xTrader...",
    "validUntil": 1717189320,
    "nonce": 1717189200042
  },
  "domain": {
    "name": "ConvallaxRFQSettlement",
    "version": "1",
    "chainId": 80002,
    "verifyingContract": "0x3E583BC44157c91F8c534372A3e91c371841107A"
  },
  "types": {
    "Order": [
      { "name": "maker", "type": "address" },
      { "name": "seriesId", "type": "uint256" },
      { "name": "optionAmount", "type": "uint256" },
      { "name": "premiumAmount", "type": "uint256" },
      { "name": "makerSelling", "type": "bool" },
      { "name": "taker", "type": "address" },
      { "name": "validUntil", "type": "uint256" },
      { "name": "nonce", "type": "uint256" }
    ]
  },
  "confirmationDeadline": "2026-06-15T12:00:11.000Z"
}
```

### `quote:confirmed` — Your quote won and was confirmed

Sent after your confirmation signature is verified.

<ResponseField name="type" type="string">
  Always `"quote:confirmed"`.
</ResponseField>

<ResponseField name="requestId" type="string">
  The quote request.
</ResponseField>

<ResponseField name="quoteId" type="string">
  Your confirmed quote's server-generated identifier.
</ResponseField>

```json theme={null}
{
  "type": "quote:confirmed",
  "requestId": "req-789",
  "quoteId": "8f3c2b1a-..."
}
```

### `quote:rejected` — Another maker won

Sent when another maker's quote won, or the request closed without your quote winning. Drop it from local state.

<ResponseField name="type" type="string">
  Always `"quote:rejected"`.
</ResponseField>

<ResponseField name="requestId" type="string">
  The quote request.
</ResponseField>

<ResponseField name="quoteId" type="string">
  Your rejected quote's server-generated identifier.
</ResponseField>

<ResponseField name="reason" type="string">
  Reason for rejection, e.g. `"another_quote_won"`.
</ResponseField>

```json theme={null}
{
  "type": "quote:rejected",
  "requestId": "req-789",
  "quoteId": "8f3c2b1a-...",
  "reason": "another_quote_won"
}
```

### `pong` — Heartbeat reply

Sent in reply to a `{ "type": "ping" }` message.

```json theme={null}
{
  "type": "pong",
  "timestamp": "2026-06-15T12:00:00.000Z"
}
```

## Maker → Server Messages

### `auth` — Authenticate

Optional if you already passed `?apiKey=` on the connection URL. Send immediately after the socket opens.

<ParamField body="type" type="string" required>
  Must be `"auth"`.
</ParamField>

<ParamField body="apiKey" type="string" required>
  Your per-MM API key.
</ParamField>

```json theme={null}
{
  "type": "auth",
  "apiKey": "mk_live_alpha_xxx"
}
```

### `ping` — Application keep-alive

```json theme={null}
{ "type": "ping" }
```

The relay replies with a `pong` message.

## Protocol Version

The current protocol version is **3**. Quote requests arrive over the [SSE quote-request stream](/api-reference/market-maker/quote-stream); quotes and confirmation signatures are submitted over [REST](/api-reference/market-maker/submit-quote). This WebSocket is post-trade only.

## Example: Listening for Wins

```javascript theme={null}
import WebSocket from 'ws';
import { Wallet } from 'ethers';

const API = 'https://api.convallax.com';
const API_KEY = process.env.MAKER_API_KEY;
const wallet = new Wallet(process.env.MM_PRIVATE_KEY);

const ws = new WebSocket(`wss://api.convallax.com/maker/v1/ws?apiKey=${API_KEY}`);
ws.on('ping', () => ws.pong());

ws.on('message', async (raw) => {
  const msg = JSON.parse(raw);

  switch (msg.type) {
    case 'connected':
      console.log(`Connected (protocol v${msg.protocolVersion}) as ${msg.makerId}`);
      break;

    case 'quote:accepted': {
      const signature = await wallet.signTypedData(msg.domain, msg.types, msg.order);
      await fetch(`${API}/v1/mm/quotes/${msg.quoteId}/confirm`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', 'X-API-Key': API_KEY },
        body: JSON.stringify({ signature }),
      });
      break;
    }

    case 'quote:confirmed':
      console.log(`Confirmed ${msg.quoteId}`);
      break;

    case 'quote:rejected':
      console.log(`Rejected ${msg.quoteId}: ${msg.reason}`);
      break;
  }
});
```
