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

# WebSocket

> Push real-time trade status and price updates to your frontend over Socket.IO: connecting, the available events, and WebSocket versus signed callbacks.

AssetPay provides a Socket.IO WebSocket connection for real-time updates. Use it to push trade status changes and price updates to your frontend without polling.

## Connecting

Connect using Socket.IO with your API key or a client token:

**With API key (merchant-level access):**

```
wss://api.assetpay.gg/socket.io/?apiKey=YOUR_API_KEY&EIO=4&transport=websocket
```

**With client token (user-level access):**

```
wss://api.assetpay.gg/socket.io/?token=CLIENT_TOKEN&EIO=4&transport=websocket
```

Both methods join the same merchant room and receive the same trade, deposit, and withdrawal events. Use whichever is more convenient for your architecture.

One exception: [market updates](#market-updates) are delivered only to API key connections where the key has the `CORE_ACCESS` scope. Client token connections never receive them.

### Socket.IO Client Example

```typescript theme={null}
import { io } from 'socket.io-client';

const socket = io('wss://api.assetpay.gg', {
  transports: ['websocket'],
  query: {
    apiKey: 'YOUR_API_KEY'  // or token: 'CLIENT_TOKEN'
  }
});

socket.on('connect', () => {
  console.log('Connected to AssetPay');
});

socket.on('disconnect', () => {
  console.log('Disconnected');
});
```

## Events

### Trade Updates

Fired whenever a trade changes status. This is the most important event for keeping your UI in sync.

```typescript theme={null}
socket.on('trade', (data) => {
  const trade = data.trade;
  console.log(`Trade ${trade.id} is now ${trade.status}`);
  // Update your UI
});
```

The `data.trade` object is the same [Trade](/docs/reference/types#trade) shape you see in callbacks and API responses.

Trade events are not game-specific. You'll receive updates for all games (CS2 and Rust) on the same connection.

### Crypto Deposit Updates

Fired when a merchant crypto deposit changes state:

```typescript theme={null}
socket.on('deposit', (data) => {
  console.log('Crypto deposit update:', data.funding);
});
```

### Crypto Withdrawal Updates

Fired when a merchant crypto withdrawal changes state:

```typescript theme={null}
socket.on('withdrawal', (data) => {
  console.log('Crypto withdrawal update:', data.funding);
});
```

### Market Updates

Fired when the market changes: an item becomes available, an existing item changes (typically its price), an item is no longer available, or the market is rebuilt. This mirrors what `GET /secure/market` returns, so you can keep a local copy current without polling.

Both CS2 (`730`) and Rust (`252490`) publish market updates on the same connection and the same envelope. Key your local state on `game` as well as `item.id`, and ignore events for games you don't offer.

Market updates require an API key connection where the key has the `CORE_ACCESS` scope. Client token connections do not receive this event.

```typescript theme={null}
socket.on('market', (event) => {
  switch (event.type) {
    case 'upsert': // event.item became available
    case 'patch':  // event.item changed (usually price)
      applyItem(event.game, event.item);
      break;
    case 'remove': // event.itemId is no longer available
      removeItem(event.game, event.itemId);
      break;
    case 'sync':   // market rebuilt; discard local state for that game and refetch
      refetchMarket(event.game); // GET /secure/market?game=<event.game>
      break;
  }
});
```

| Field    | Type   | Description                                                                                                   |
| -------- | ------ | ------------------------------------------------------------------------------------------------------------- |
| `game`   | number | App id: `730` for CS2, `252490` for Rust                                                                      |
| `source` | string | Market lane. Always `"internal"`                                                                              |
| `type`   | string | `"upsert"`, `"patch"`, `"remove"`, or `"sync"`                                                                |
| `item`   | object | Present on `upsert` and `patch`. The full item, same shape as `/secure/market` items, priced for your account |
| `itemId` | string | Present on `remove`. The `id` of the item to drop                                                             |

Notes on applying events:

* `upsert` and `patch` both carry the full item. Apply either as an idempotent upsert keyed on `item.id`; the distinction only signals whether the item is new or changed. Not every game emits both, so never branch on which of the two you received.
* A `remove` can arrive for an item you never saw. Ignore it in that case.
* On `sync`, your local view for that `game` may be arbitrarily stale. Refetch `GET /secure/market?game=<game>` and rebuild from the response. A `sync` affects only the game it names; leave the other game's state alone.
* A price change does not always arrive as a `patch`. Where an item's `id` is tied to the price it is offered at, the same change arrives as a `remove` of the old entry followed by an `upsert` of a new one. Apply events in the order received.
* Prices are per merchant account, so the `offer.price` you receive already reflects your fee. Still validate the price at purchase time as described in [Market](/docs/guides/market#keeping-prices-updated).

## WebSocket vs Callbacks

Both WebSocket and callbacks deliver trade updates. They serve different purposes:

|                 | WebSocket                                   | Callbacks                          |
| --------------- | ------------------------------------------- | ---------------------------------- |
| **Direction**   | Push to your frontend/client                | Push to your backend               |
| **Use for**     | UI updates, real-time UX                    | Balance operations, business logic |
| **Reliability** | Best-effort (can miss events on disconnect) | Guaranteed delivery with retries   |
| **State**       | Stateful connection                         | Stateless HTTP POST                |

**Use both.** WebSocket for instant UI feedback, callbacks for the authoritative balance updates. Don't process balance changes based on WebSocket events alone.

## Connection Notes

* Only the `websocket` transport is supported (no HTTP long-polling fallback)
* The connection authenticates once on connect. If your token expires, reconnect with a fresh one.
* All events are scoped to your merchant account. You only see your own trades.
