> For the complete documentation index, see [llms.txt](https://mute-sh.gitbook.io/mute.sh/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://mute-sh.gitbook.io/mute.sh/api/api-reference/l1-votes.md).

# L1 Governance Votes (Enterprise)

The earliest possible signal on HIP-4: a live feed of the **pending HyperCore L1 validator votes** that create and settle markets. When validators are voting to settle an outcome, you see it here — voter by voter, stake by stake — before the settlement lands on-chain. Enterprise tier only, delivered over SSE or WebSocket.

```bash
# SSE
curl -N "https://api.mute.sh/v1/l1-votes/stream?apikey=$MUTE_KEY"
# WebSocket
websocat "wss://api.mute.sh/v1/l1-votes/ws?apikey=$MUTE_KEY"
```

## The event model

Both transports deliver the same `VoteEvent` JSON. On connect you get one **`snapshot` event per currently-pending action** (skip with `?snapshot=false` on SSE), then live deltas. A real captured snapshot:

```json
{
  "type": "snapshot",
  "seq": "0",
  "action": {
    "kind": "settle_outcome",
    "key": "settleOutcome:696",
    "variant": "settleOutcome",
    "quote_token": 0,
    "question_name": "",
    "question_description": "",
    "question_id": 0,
    "legs": [
      { "outcome": 696, "settle_fraction": "1", "details": "FIFA officially declared Mexico the winner of the Game." }
    ]
  },
  "voters": [
    { "address": "0x000000000056f99d36b6f2e0c51fd41496bbacb8", "name": "ValiDAO", "stake": "659111919868595", "is_active": true, "is_jailed": false }
  ],
  "added": [],
  "quorum_reached": true,
  "voter_count": 13,
  "stake_voted": "11787210534593377",
  "stake_active_total": "43815897035188101",
  "stake_pct": 26.9,
  "expire_time_ms": 1783673345136,
  "node_seen_us": "1783176138308379"
}
```

That's a World Cup settlement mid-vote: validators (with display names and stakes) voting `settle_fraction: "1"` for outcome 696, with the resolution reason in `details`.

### Event types

| `type`            | Meaning                                                 | State action               |
| ----------------- | ------------------------------------------------------- | -------------------------- |
| `snapshot`        | a currently-pending action (connect replay; `seq: "0"`) | **upsert** by `action.key` |
| `action_opened`   | a new vote began                                        | upsert                     |
| `vote_cast`       | validator(s) voted — the new voters are in `added[]`    | upsert                     |
| `quorum_reached`  | the vote crossed quorum                                 | upsert                     |
| `action_resolved` | the action executed — it left the pending set           | **remove** `action.key`    |
| `action_expired`  | the vote timed out (`expire_time_ms`)                   | remove                     |

Maintain a map keyed by `action.key`: upsert on the first four, delete on the last two. Snapshots can re-arrive after an upstream resync — the upsert semantics make that harmless. Treat unrecognized `type`/`kind` strings as no-ops (an `unspecified` value exists for forward compatibility).

### Action kinds

`action.kind` ∈ `register_question` | `settle_question` | `settle_outcome` | `other`. Settlements carry `legs[]` — the outcome id, the proposed `settle_fraction` (`"1"` won / `"0"` lost, as a string), and the human-readable resolution rationale. Registrations are new markets being born.

### ⚠ Big integers are strings

`stake`, `stake_voted`, `stake_active_total`, `seq`, and `node_seen_us` are **strings** on the wire — they're 64-bit values that exceed `Number.MAX_SAFE_INTEGER` (the live `stake_active_total` is \~4.4×10¹⁶, five times past the safe-integer limit). Parse with `BigInt(...)`, never `Number(...)`. Two fields are safe JS numbers: `expire_time_ms` and the pre-derived `stake_pct` (computed server-side in BigInt arithmetic precisely so you don't have to).

### Field notes

* `voters[]` is the **full current voter set** on every event (enriched with validator names and jail status); `added[]` isolates the delta on `vote_cast` events only.
* `seq` is a per-stream monotonic counter (as a string) for live events — use it for gap detection; snapshots are always `"0"`.
* `node_seen_us` is when the node-adjacent collector saw the event (epoch microseconds) — your end-to-end latency reference.
* There is **no topic filtering** — every enterprise client receives every pending action; filter by `action.kind` or `legs[].outcome` client-side.

## Transport details

**SSE** (`/v1/l1-votes/stream`): frames as `event: <type>` + `data: <VoteEvent>`; `: connected <ts>` comment on open, `: ping <ms>` heartbeat \~every 25 s. `?snapshot=false` skips the replay.

**WebSocket** (`/v1/l1-votes/ws`): the same `VoteEvent` objects as flat JSON text frames (the `type` field discriminates), plus `{"type":"ping","ts":…}` heartbeats and a `{"type":"error","error":"stream max duration reached"}` frame before the 30-minute close.

Shared limits: 500 concurrent streams globally, **3 per key/IP**, **30-minute lifetime** — reconnect and let the snapshot replay rebuild your state (that's what it's for).

## Auth on this surface

| You send                    | SSE result                  | WS result            |
| --------------------------- | --------------------------- | -------------------- |
| Enterprise key (`?apikey=`) | `200` + stream              | `101` + frames       |
| Valid lower-tier key        | `403` (Enterprise required) | edge-level rejection |
| Invalid key                 | `401` JSON                  | edge-level rejection |
| No key                      | `403` JSON                  | **opaque `502`**     |

Note the WS failure mode: auth rejections happen during the upgrade and surface through the edge as a generic `502` — if your WS client logs 502, verify the `apikey` first (the SSE endpoint with the same key will give you the real `401`/`403` reason).

## Why this feed exists

Settlement votes precede on-chain settlement by minutes. A `settle_outcome` action with `quorum_reached: true` tells you *how a market is about to resolve* while its coins are still trading — this is the data source for settlement-front-running protection, instant "result is in" UX, and market-resolution alerting. Pair it with [`/v1/outcomes/:id`](/mute.sh/api/api-reference/outcomes.md) to resolve the `legs[].outcome` id to a market.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://mute-sh.gitbook.io/mute.sh/api/api-reference/l1-votes.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
