On this page
- Endpoint & envelope
- Channel categories
- Channel reference
- artery:stream:event:<universalId>
- artery:stream:arbitrage:<universalId>
- artery:stream:trade:<universalId>
- artery:stream:trade:<provider>
- artery:stream:user-trade:<orgId>
- artery:stream:anomaly:<universalId>
- artery:stream:anomaly:<kind> (firehose)
- artery:stream:settlement_dispute:<universalId>
- artery:stream:settlement:<universalId>
- Raw upstream feeds (advanced)
- Subscription rules
- Backpressure
- See also
WebSocket Channel Catalog
This page enumerates every channel exposed on /v1/stream. For the
wire protocol (subscribe / unsubscribe / ping / event frames) see the
streaming concept; for the internal architecture
see streaming architecture.
Endpoint & envelope
wss://api.artery.questflow.ai/v1/stream?token=art_live_<your-key>
Every server-pushed message is one of:
jsonc// → subscribe ack
{ "type": "subscribed", "subscription_id": "sub_01HMQ5..." }
// → event frame
{
"type": "event",
"subscription_id": "sub_01HMQ5...",
"data": {
"type": "<channel-specific envelope type>",
"provider": "polymarket" | "kalshi" | "hyperliquid_perp" | "hyperliquid_hip4",
"marketId": "<provider market id or universalId>",
"payload": { /* channel-specific */ },
"receivedAt": 1747123338220
}
}
// → heartbeat (every 30s)
{ "type": "pong" }
// → error
{ "type": "error", "error": "<message>" }All channels require the stream scope. The admin scope grants
implicit access to every channel including private user-trade feeds.
Channel categories
| Category | Pattern | Cardinality |
|---|---|---|
| Universal event spread | artery:stream:event:<universalId> | One channel per event |
| Universal event arbitrage | artery:stream:arbitrage:<universalId> | One channel per event |
| Per-event trades | artery:stream:trade:<universalId> | One channel per event |
| Per-provider trade firehose | artery:stream:trade:<provider> | 4 channels (one per provider) |
| Per-org private fills | artery:stream:user-trade:<orgId> | One channel per tenant |
| Per-event anomaly | artery:stream:anomaly:<universalId> | One channel per event |
| Anomaly firehose (by kind) | artery:stream:anomaly:<kind> | 4 channels (one per kind) |
| Anomaly firehose (all) | artery:stream:anomaly:all | 1 global channel |
| UMA dispute observations | artery:stream:settlement_dispute:<universalId> | One channel per event |
| Settlement landed | artery:stream:settlement:<universalId> | One channel per event |
| Raw upstream feeds | artery:stream:<provider>:<providerMarketId> | Per upstream feed |
Channel reference
artery:stream:event:<universalId>
Cross-platform spread snapshots for one universal event.
| Field | Value |
|---|---|
| Scope | stream |
data.type | "event.spread" |
| Payload | SpreadSnapshot — same shape as GET /v1/events/:id/spread |
| Cadence | Every 5s tick and push-driven from upstream WS, debounced — only republishes when at least one leg's yesMid moved ≥ $0.005 |
jsonc{
"type": "event",
"data": {
"type": "event.spread",
"provider": "polymarket",
"marketId": "evt_01HMPKW8...",
"payload": {
"universalId": "evt_01HMPKW8...",
"legs": [
{ "provider": "polymarket", "yesBid": 0.42, "yesAsk": 0.45, "yesMid": 0.435 },
{ "provider": "kalshi", "yesBid": 0.39, "yesAsk": 0.42, "yesMid": 0.405 }
],
"fetchedAt": "2026-05-13T08:42:18Z"
},
"receivedAt": 1747123338220
}
}artery:stream:arbitrage:<universalId>
Fires only when a profitable cross-provider or negation-pair arbitrage edge is detected on the latest spread snapshot.
| Field | Value |
|---|---|
| Scope | stream |
data.type | "event.arbitrage" |
| Payload | CrossProviderArbitrage | NegationPairArbitrage — same shape as GET /v1/events/:id/arbitrage |
| Trigger | On every spread tick that yields netEdge > 0 |
Inspect payload.kind to distinguish: "cross_provider" has
buyProvider / sellProvider; "negation_pair" has yesProvider /
noProvider.
artery:stream:trade:<universalId>
Public per-event trade fanout. Every observed fill across every linked market on this universal event.
| Field | Value |
|---|---|
| Scope | stream |
data.type | "trade.observed" |
| Payload | TradeRecord — same shape as GET /v1/events/:id/trades[] |
| Trigger | Each Polymarket Exchange.OrderFilled (Polygon) + each Hyperliquid trades WS event |
jsonc{
"type": "event",
"data": {
"type": "trade.observed",
"provider": "polymarket",
"marketId": "evt_01HMPKW8...",
"payload": {
"provider": "polymarket",
"marketId": "0xCONDITION_ID",
"ts": "2026-05-13T08:50:01.220Z",
"side": "buy",
"price": 0.421,
"qty": 1500,
"maker": "0xabc...",
"taker": "0xdef...",
"txHash": "0x123...",
"logIndex": 4
},
"receivedAt": 1747123801222
}
}artery:stream:trade:<provider>
Per-provider trade firehose. Same payload shape as the per-event channel, but unfiltered by event. Suitable for ETL pipelines.
| Pattern | Provider |
|---|---|
artery:stream:trade:polymarket | Polymarket Polygon |
artery:stream:trade:hyperliquid_perp | HL perps |
artery:stream:trade:hyperliquid_hip4 | HL HIP-4 binary |
artery:stream:trade:kalshi | Kalshi (planned) |
artery:stream:user-trade:<orgId>
Private per-org channel. Mirrors every fill where one of the org's bound wallets was maker or taker.
| Field | Value |
|---|---|
| Scope | stream (auth checks orgId matches caller's org) |
data.type | "user.fill" |
| Payload | TradeRecord (same as /v1/me/fills[]) |
| Trigger | Each upstream trade whose maker or taker matches any customer_wallets.address for orgId |
Subscribers to other orgs' channels receive { "type": "error", "error": "forbidden_scope" } on subscribe.
artery:stream:anomaly:<universalId>
Per-event anomaly stream. Fires for all four kinds
(trade_volume_spike, spread_widening, wallet_burst,
settlement_delay) — filter client-side on payload.kind.
| Field | Value |
|---|---|
| Scope | stream |
data.type | "anomaly.detected" |
| Payload | AnomalyRecord (same as /v1/events/:id/anomaly-history[]) |
| Trigger | On each detection by any of the four detectors |
artery:stream:anomaly:<kind> (firehose)
Global anomaly firehose, partitioned by kind:
| Channel | Kind |
|---|---|
artery:stream:anomaly:trade-volume | trade_volume_spike |
artery:stream:anomaly:spread-widening | spread_widening |
artery:stream:anomaly:wallet-burst | wallet_burst |
artery:stream:anomaly:settlement-delay | settlement_delay |
The channel suffix uses kebab-case (trade-volume, spread-widening) but the
payload.kind field uses snake_case (trade_volume_spike, spread_widening). Don't compare
the channel suffix to payload.kind directly.
Plus artery:stream:anomaly:all — every anomaly regardless of kind.
artery:stream:settlement_dispute:<universalId>
UMA on-chain dispute observations for Polymarket-linked events.
| Field | Value |
|---|---|
| Scope | stream |
data.type | "settlement.dispute" |
| Payload | DisputePhaseRecord (same shape as /v1/events/:id/dispute-history[]) |
| Trigger | Each propose / dispute / settle UMA event seen by the Polygon RPC listener |
Plus the provider-firehose alias artery:stream:settlement_dispute:polymarket.
artery:stream:settlement:<universalId>
Fires once when a provider resolution for the event is observed (the
authoritative payoff signal — typically Kalshi settled or HL HIP-4
resolveOutcome).
| Field | Value |
|---|---|
| Scope | stream |
data.type | "settlement.landed" |
| Payload | ResolutionRecord (same shape as /v1/events/:id/settlement.records[]) |
| Trigger | Once per provider per event when settlement is recorded |
Downstream services (e.g. PnlFinalizationService) consume this to
mark fills realized.
Raw upstream feeds (advanced)
For callers that want the upstream payload pre-Artery-normalization:
| Channel | What |
|---|---|
artery:stream:hyperliquid_perp:* | All HL allMids updates (~50ms cadence) |
artery:stream:hyperliquid_perp:<coin> | HL l2Book for one coin |
artery:stream:hyperliquid_hip4:* | All HL HIP-4 binary-outcome updates |
artery:stream:polymarket:<token_id> | Polymarket price_change + last_trade_price per token |
artery:stream:kalshi:<ticker> | Kalshi orderbook_delta (planned, RSA-gated) |
The payload here is the upstream JSON shape, not Artery's
normalized type. See per-provider notes in the
streaming concept.
Subscription rules
- Channels are flat strings — no glob expansion is supported in
subscribe(use*only as the explicit channel suffix for provider firehoses). - Multiple subscriptions are independent; unsubscribing one doesn't affect the others.
- Up to 1,000 active subscriptions per connection
(
subscription_limiterror on overflow). - Concurrent connections per key follow your tier table.
Backpressure
| Layer | Bound | Drop policy |
|---|---|---|
| Per-connection send queue | 5 MiB | drop oldest, then close on overflow |
| Per-connection subs | 1000 | reject new subscribe with subscription_limit |
| Heartbeat idle | 60s | server closes connection (code 1001) |
See also
- Streaming concept — wire protocol overview
- Streaming architecture — leader-lock + pub/sub internals
- Streaming quickstart recipe — connect, subscribe, parse
- Examples — one-shot CLI test