> ## 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.

# Authentication

> How market makers authenticate to the Convallax relay with per-MM API keys — and why takers need no API key at all.

Convallax has two very different kinds of participants, and they authenticate in two very different ways:

<CardGroup cols={2}>
  <Card title="Market makers" icon="key">
    Authenticate every maker channel with an **API key you generate yourself** in the dashboard. The key maps to a stable `makerId` and all of your quotes are attributed to it.
  </Card>

  <Card title="Takers (traders)" icon="wallet">
    Need **no API key**. REST trading endpoints are open, and your authorization is the on-chain `fill()` call signed by your own wallet.
  </Card>
</CardGroup>

There is also a small set of **account endpoints** (profile, API-key management) authenticated with a **Privy access token** rather than an API key — see [Account endpoints](#account-endpoints-privy-jwt) below.

<Note>
  The **discovery / read endpoints** — `GET /v1/markets`, `GET /v1/series`, and `GET /v1/series/{seriesId}` — are fully **public**. They require **no API key and no Privy token**, even if you are a market maker. Sending an `X-API-Key` header to them has no effect (it is not checked). Only the `/v1/mm/*` maker channels require your API key.
</Note>

## Market Maker API Keys

Each market maker is an independent entity with its own wallet and USDC. To quote on the relay, you authenticate with an API key. The key identifies your `makerId` — a stable identifier that every quote you submit is attributed to.

There is **no separate market-maker role**: any signed-in user can generate a key and begin quoting.

Makers integrate over a **three-channel transport**, and the same API key authenticates all three. How you present the key depends on the channel:

| Channel                                                                | Transport | How to authenticate                         |
| ---------------------------------------------------------------------- | --------- | ------------------------------------------- |
| [Quote-request stream](/guides/quote-stream)                           | SSE       | `X-API-Key` header (or `?apiKey=`)          |
| [Quote submission + confirm](/api-reference/market-maker/submit-quote) | REST      | `X-API-Key` header (or `?apiKey=`)          |
| [Post-trade events](/guides/websocket)                                 | WebSocket | `?apiKey=` on the URL, or an `auth` message |

### Generating a key

API keys are **self-serve** from the Convallax dashboard:

<Steps>
  <Step title="Sign in">
    Open the dashboard and connect your wallet (this signs you in via Privy).
  </Step>

  <Step title="Open Settings → API Keys">
    Go to **Settings** in the top nav, then the **API Keys** tab.
  </Step>

  <Step title="Generate and store the key">
    Click **Generate key**. The full key is shown **once** — copy it immediately and store it securely. You can revoke a key at any time from the same screen.
  </Step>
</Steps>

The same actions are available programmatically via the [account endpoints](#account-endpoints-privy-jwt) (`POST /v1/user/api-keys`).

### Key format

Keys look like `mk_live_<random>` and are mapped server-side to your account's `makerId` (for example `mm_9f8e7d6c5b`). Treat your key as a secret — anyone holding it can submit quotes attributed to your `makerId`. The relay stores only a hash of your key; if you lose it, revoke it and generate a new one.

<Warning>
  Your API key only authorizes **quoting**. It can never move funds. On-chain settlement always requires a signature from your own wallet key (see [Trade Lifecycle](/guides/lifecycle)). Keep your API key and your wallet key in separate, secure storage.
</Warning>

## Authenticating the SSE Stream & REST Endpoints

The [quote-request SSE stream](/guides/quote-stream) and the REST quote endpoints ([submit](/api-reference/market-maker/submit-quote) and [confirm](/api-reference/market-maker/confirm-quote)) authenticate with the **`X-API-Key`** header:

```
X-API-Key: mk_live_alpha_xxx
```

If your client cannot set custom headers (for example, a browser `EventSource`), pass the key as a query parameter instead:

```
GET https://api.convallax.com/v1/mm/quote-requests/stream?apiKey=mk_live_alpha_xxx
```

```bash theme={null}
curl -X POST https://api.convallax.com/v1/mm/quotes \
  -H "X-API-Key: mk_live_alpha_xxx" \
  -H "Content-Type: application/json" \
  -d '{ "requestId": "req-789", "quote": { ... } }'
```

## Authenticating on the WebSocket

The [post-trade WebSocket](/guides/websocket) uses a different mechanism. There are two equivalent ways to present your API key when connecting to `wss://api.convallax.com/maker/v1/ws`.

<Steps>
  <Step title="Option A — query parameter">
    Append your key to the connection URL:

    ```
    wss://api.convallax.com/maker/v1/ws?apiKey=mk_live_alpha_xxx
    ```

    This is the simplest approach and authenticates you the moment the socket opens.
  </Step>

  <Step title="Option B — auth message">
    Connect without a key, then send an `auth` message **immediately** after the connection opens:

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

    Use this when you prefer to keep the key out of the connection URL (for example, to avoid it appearing in proxy or access logs).
  </Step>
</Steps>

### Successful authentication

On a valid key, the server replies with a `connected` message that echoes your resolved `makerId`:

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

From this point on, every quote you submit (over REST) is attributed to `mm-alpha`. You do not need to include your `makerId` — the relay derives it from your authenticated API key.

### Invalid authentication

If the API key is missing or invalid (when the relay is configured with keys), the server **closes the socket with code `4001`**. Reconnect with a valid key.

<Note>
  Re-POSTing to `/v1/mm/quotes` for the same `requestId` **updates** (replaces) your previous quote. Because quotes are attributed to your authenticated `makerId`, you cannot impersonate or overwrite another maker's quotes.
</Note>

## Open Dev Mode

If the relay is started **without any maker keys configured and without a database**, it runs in **OPEN dev mode**: it accepts anyone, assigns an anonymous `makerId`, and does not require an `apiKey`. This is intended for local development and testing against your own relay instance.

In production (`wss://api.convallax.com`), keys are always required.

## Account Endpoints (Privy JWT)

Managing your profile and API keys uses a different credential: a **Privy access token**, not an API key. The dashboard obtains this token when you sign in and sends it as a bearer token:

```
Authorization: Bearer <privy-access-token>
```

These endpoints are authenticated this way:

| Endpoint                                                              | Purpose                      |
| --------------------------------------------------------------------- | ---------------------------- |
| [`GET /v1/user/profile`](/api-reference/user/get-profile)             | Your account + `makerId`     |
| [`GET /v1/user/api-keys`](/api-reference/user/list-api-keys)          | List active keys             |
| [`POST /v1/user/api-keys`](/api-reference/user/create-api-key)        | Create a key (returned once) |
| [`DELETE /v1/user/api-keys/{id}`](/api-reference/user/revoke-api-key) | Revoke a key                 |

<Note>
  The two credentials are distinct: a **Privy token** proves who you are (to manage your account), while an **API key** authorizes maker traffic. Creating an API key requires a Privy token; using it to quote does not.
</Note>

## Server Configuration

The self-serve key store requires a Postgres database (`DATABASE_URL`) and Privy server credentials (`PRIVY_APP_ID`, `PRIVY_APP_SECRET`). In addition, first-party / ops makers can be configured with static environment keys that work alongside self-serve keys:

<ParamField path="MAKER_API_KEYS" type="JSON array">
  The recommended way to configure multiple makers. Each entry maps an API key to a stable `makerId`:

  ```json theme={null}
  [
    { "apiKey": "mk_live_alpha_xxx", "makerId": "mm-alpha" },
    { "apiKey": "mk_live_beta_yyy",  "makerId": "mm-beta" }
  ]
  ```
</ParamField>

<ParamField path="MAKER_API_KEY" type="string">
  Legacy single-key mode. Still supported for backwards compatibility — the key maps to the `makerId` `"default"`.
</ParamField>

If neither variable is set **and** no database is configured, the relay runs in [OPEN dev mode](#open-dev-mode). Once a database is attached, anonymous access is disabled and a valid key is always required.

## Takers Need No API Key

Traders (takers) never authenticate with an API key. The REST trading endpoints are open:

* `POST /quote-requests` — open a live quote request
* `GET /quote-requests/:id/stream` — stream the live best quote (SSE; preferred)
* `GET /quote-requests/:id/quotes` — poll for the best quote (fallback)
* `POST /quote-requests/:id/commit` — commit to the best quote

Your authorization as a taker is enforced **on-chain**: the signed `Order` you receive at commit restricts settlement to your wallet, and only that wallet can call `fill()` on `ConvallaxRFQSettlement`. No funds move until you sign and broadcast that transaction yourself.

<Info>
  See the [Quote Request Stream guide](/guides/quote-stream) and [Post-Trade WebSocket guide](/guides/websocket) for the channel references, and the [Market Maker guide](/guides/market-maker) for the complete integration flow including order signing.
</Info>
