Skip to content

User Channel

This channel pushes notifications scoped to a single account whenever orders are placed, updated, cancelled, or matched into trades. Access requires L2 API credentials, supplied either as a query parameter or through an in-band JSON authentication message.

wss://api.openfish.fun/ws/user

Two authentication methods are supported:

Open the WebSocket without any query parameters, then send a JSON authentication payload as the very first message. The server allows a 10-second window before timing out:

{
"auth": {
"apiKey": "9180014b-33c8-9240-a14b-bdca11c0a465",
"secret": "your-api-secret",
"passphrase": "your-passphrase"
},
"markets": []
}

A successful handshake returns:

{"type": "auth", "status": "ok"}

If the credentials are rejected:

{"error": "invalid auth message"}

If no authentication payload arrives within the deadline:

{"error": "auth timeout"}

For backward compatibility, pass the API key as a query parameter:

wss://api.openfish.fun/ws/user?token={api_key}

The token must be a valid API key UUID. If invalid, the server responds with {"error": "invalid token"} and closes the connection.

Once authenticated, the server binds a dedicated broadcast subscription to your API key. Every event delivered on the connection belongs exclusively to the authenticated account — no cross-user leakage is possible.

A text message "PING" arrives from the server every 10 seconds. Respond with the text message "PONG" to prevent the connection from being dropped.

Note: This uses text messages, not WebSocket-level ping/pong frames.

Produced whenever an order is placed on the book, partially filled, or removed.

FieldTypeDescription
typestring"order"
order_idstringOrder ID (UUID)
event_typestring"PLACEMENT", "UPDATE", or "CANCELLATION"
statusstring"LIVE", "MATCHED", "CANCELED"
marketstringCondition ID
asset_idstringToken ID
sidestring"BUY" or "SELL"
pricestringOrder price
original_sizestringOriginal size
size_matchedstringAmount matched so far
outcomestring"YES" or "NO"
order_typestring"GTC", "FOK", "GTD"
ownerstringAPI key of the order owner
timestampintegerUnix timestamp

Order Placement:

{
"type": "order",
"order_id": "0xff354cd7ca7539dfa9c28d90943ab5779a4eac34b9b37a757d7b32bdfb11790b",
"event_type": "PLACEMENT",
"status": "LIVE",
"asset_id": "52114319501245915516055106046884209969926127482827954674443846427813813222426",
"market": "0xbd31dc8a20211944f6b70f31557f1001557b59905b7738480ca09bd4532f84af",
"outcome": "YES",
"order_type": "GTC",
"side": "SELL",
"price": "0.57",
"original_size": "10",
"size_matched": "0",
"owner": "9180014b-33c8-9240-a14b-bdca11c0a465",
"timestamp": 1672290687
}

Partial Fill:

{
"type": "order",
"order_id": "0xff354cd7...",
"event_type": "UPDATE",
"status": "LIVE",
"size_matched": "5",
"original_size": "10",
"timestamp": 1672290700
}

Cancellation:

{
"type": "order",
"order_id": "0xff354cd7...",
"event_type": "CANCELLATION",
"status": "CANCELED",
"timestamp": 1672290710
}

Produced when one of your orders participates in a match, and again each time the resulting trade advances through its settlement lifecycle.

FieldTypeDescription
typestring"trade"
trade_idstringTrade UUID
taker_order_idstringTaker’s order ID
marketstringCondition ID
asset_idstringToken ID
sidestring"BUY" or "SELL"
pricestringTrade price
sizestringTrade size
feestringFee amount
statusstring"MATCHED", "CONFIRMED", "FAILED"
trader_sidestring"TAKER" or "MAKER" — your role in the trade
maker_ordersarrayDetails of matched maker orders
timestampintegerUnix timestamp
{
"type": "trade",
"trade_id": "28c4d2eb-bbea-40e7-a9f0-b2fdb56b2c2e",
"taker_order_id": "0x06bc63e3...",
"asset_id": "52114319...",
"market": "0xbd31dc8a...",
"side": "BUY",
"price": "0.57",
"size": "10",
"fee": "0.0057",
"status": "MATCHED",
"trader_side": "TAKER",
"maker_orders": [
{
"order_id": "0xff354cd7...",
"owner": "9180014b-...",
"maker_address": "0xf39F...",
"matched_amount": "10"
}
],
"timestamp": 1672290701
}

Every trade moves through a sequence of statuses as it progresses from in-memory match to on-chain finality:

MATCHED --> MINED --> CONFIRMED
| ^
v |
RETRYING ----+
|
v
FAILED
StatusTerminalDescription
MATCHEDNoTrade matched by the CLOB engine, sent to executor
MINEDNoTransaction observed on-chain, not yet finalized
CONFIRMEDYesTransaction achieved finality
RETRYINGNoTransaction failed (revert/reorg), being resubmitted
FAILEDYesTrade permanently failed after retry exhaustion

Each status transition triggers a new trade event on the WebSocket.

Internally, the broadcast system maintains a DashMap keyed by API key. Every authenticated connection receives its own tokio::sync::broadcast channel, guaranteeing that events published for one user never leak to another. When a client disconnects, its channel is cleaned up automatically.

use openfish_client_sdk::ws::UserSocket;
use futures_util::StreamExt;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let api_key = "your-api-key-uuid";
let url = format!("wss://api.openfish.fun/ws/user?token={}", api_key);
let mut ws = UserSocket::connect(&url).await?;
while let Some(event) = ws.next().await {
match event.event_type.as_str() {
"order" => {
println!(
"Order {}: {} {} @ {}",
event.r#type, event.side, event.original_size, event.price
);
}
"trade" => {
println!(
"Trade {}: {} {} @ {} [{}]",
event.id, event.side, event.size, event.price, event.status
);
}
_ => {}
}
}
Ok(())
}
  • Keep your API key out of client-side browser code. The user channel is designed for server-side applications and native clients only.
  • API keys are validated as UUIDs against the database each time a connection is opened. Rotate credentials immediately if you suspect they have been compromised.
  • Authentication failures cause the server to close the WebSocket right away, preventing any events from being sent before the connection terminates.