vike

Alert feed and webhooks

GET /v1/alerts/events returns the events your alert rules produced, oldest first, paged by cursor. This page also gives the webhook body and every event shape. The route appears in no OpenAPI spec.

Every alert rule you own produces events: one per transfer, fill, liquidation, index crossing or liquidation-risk crossing it matched. An event is sent to the rule's channels and is also kept for 3 days in a feed you can read, over REST at GET /v1/alerts/events or over MCP with alerts_events. Rules are made with the MCP tools or under Account → Alerts; what each rule type watches is on Alerts. This page is the contract for what comes back: the feed route, the webhook body and the shape of each event.

This route is in no OpenAPI spec

GET /v1/alerts/events reads your own account's events, not one of the four data products, so it is not in the OpenAPI document and the generated route tables elsewhere on this site do not list it. It is a real route on https://data.vike.io, with the same key as the rest of /v1; this page is its reference.

Endpoint

GET https://data.vike.io/v1/alerts/events

GET https://data.vike.io/v1/alerts/events

Send the same X-API-Key header as every other /v1 route (Authentication). The answer is always about the key's own account: there is no parameter that names another user, and a rule_id that belongs to someone else gives an empty page. A call costs 1 credit and counts against the hourly and per-minute limits like any other /v1 call (Rate limits); a call that is answered with an error costs nothing.

ParameterWhereMeaning
cursorqueryThe previous page's next_cursor, passed back verbatim. Omit it (or send it empty) to start at the oldest event still kept. It is opaque: do not build or edit one.
limitqueryEvents per page, 1 to 500, default 100. A number outside the range is clamped, not refused.
rule_idqueryOnly the events of this alert rule (its id from alerts_list).

The answer

Example response
{
  "events": [
    {
      "id": 1042,
      "rule_id": 77,
      "alert_label": "Whale BTC fills",
      "alert_type": "hl_wallet",
      "created_at": "2026-10-28T09:14:41.200Z",
      "event": {
        "schema": "vike.event.v1",
        "event_id": "hl.fills:0x1111111111111111111111111111111111111111:1793178870482:123456789",
        "source": "hl.fills",
        "kind": "fill",
        "ts": "2026-10-28T09:14:30.482Z",
        "cursor": "1793178870482-00000000000123456789",
        "chain": "hyperliquid",
        "subject": { "wallet": "0x1111111111111111111111111111111111111111", "symbol": "BTC" },
        "data": {
          "action": "open", "side": "buy", "size_usd": 1250000.0, "size_token": 12.5, "price": 100000.0,
          "closed_pnl": 0.0, "start_position": 0.0, "position_after": 12.5, "crossed": true,
          "trade_id": 123456789, "dex": "HL"
        },
        "labels": { "pnl_cohort": "2x smart", "pnl_cohort_basis": "live", "pnl_cum_usd": 4210000.0, "size_cohort": "3x whale" }
      },
      "delivery": [ { "channel": "webhook", "channel_key": "12", "status": "sent" } ]
    }
  ],
  "next_cursor": "eyJpZCI6MTA0Mn0",
  "has_more": false
}

This example shows the shape; its values are made up. The wallet, ids and prices are not a real capture.

FieldWhat it is
eventsThe page, oldest first. Empty when there is nothing newer than cursor.
next_cursorPass it back as cursor for the next page. An empty page returns the cursor you sent, so keep it and ask again.
has_moretrue when more events follow this page.

Each element of events is one event of one rule:

FieldWhat it is
idThe feed's own row number for this (rule, event) pair. Not the event's id: use event.event_id for that.
rule_id, alert_label, alert_typeThe rule that matched, its name and its type.
created_atWhen the event was recorded, UTC, milliseconds, spelled like event.ts.
eventWhat happened: exactly the event object the webhook carries, below.
deliveryOne entry per channel the event was sent to: channel (email or webhook), channel_key (the channel's id as alerts_list_channels shows it, or user_email for your account address) and status. An empty list means the rule has no channel: the feed is its channel.

A delivery's status is pending (not tried yet), retry (a try failed, another is scheduled), sent, expired (not delivered within 72 hours), skipped (the channel was deleted, disabled or changed before the send) or unsupported (the channel cannot carry this event). It is read when you ask, so it moves on between polls.

Paging

  • Events come oldest first, so a poller walks forward: pass next_cursor back as cursor.
  • A page shorter than limit with has_more: false means you have caught up. Poll again later with the same next_cursor; an empty page returns it unchanged.
  • An event is in the feed as soon as it is recorded, which is before its email or webhook is sent. Events appear in the order they are numbered, so a poller that always passes back the last next_cursor misses none and sees none twice.
  • Events are kept 3 days. A cursor older than that does not fail: the feed continues from the oldest event still kept.
  • Deleting a rule for good (alerts_delete with hard: true) removes its events. Pausing it keeps them.
  • Only events of your own rules appear.

Errors

StatusWhen
400cursor is not a valid cursor. Pass back a next_cursor unchanged, or start again without one.
401No credential, or one that is not valid.
422limit or rule_id is not an integer.
429The hourly budget or the per-minute cap is spent; see Rate limits.
503The feed could not be read. Ask again.

The webhook body

A webhook channel receives the same event as a POST. Every rule type uses one envelope; the signature and the retries are on alerts_register_webhook.

Webhook body (fill)
{
  "schema": "vike.alert.v1",
  "alert_id": 77,
  "alert_label": "Whale BTC fills",
  "alert_type": "hl_wallet",
  "triggered_at": "2026-10-28T09:14:30.482000+00:00",
  "event": { "schema": "vike.event.v1", "event_id": "hl.fills:0x1111111111111111111111111111111111111111:1793178870482:123456789", "...": "as in the feed" }
}
FieldWhat it is
schemavike.alert.v1.
alert_id, alert_label, alert_typeThe rule: its id, its name (alert #<id> when it has none) and its type.
triggered_atThe event's time, ISO 8601 with a +00:00 offset.
eventThe event, as in the feed.

For a transfer event the body also carries the flat keys the earlier webhook format had, so a receiver written for it keeps working: chain, token (the symbol, empty when the token has none), value_usd (null when the transfer has no USD value), from_address, to_address and tx_hash. Those three are bare lowercase hex without 0x; inside event the same values keep their 0x.

Two request headers identify the event: X-Vike-Event-Id (the event's event_id) and X-Vike-Attempt (1 for the first try, then 2, 3 and so on). Delivery is at least once: a send that was cut off can arrive twice, so dedupe on event_id.

Event shapes

Every event, in the feed or in a webhook, has the same ten fields, and kind says which data and subject to expect.

FieldWhat it is
schemavike.event.v1.
event_idA unique string that always begins with its source and a colon. Stable: the same event always has the same id. At most 160 characters.
sourcechain.transfers, hl.fills, hl.liquidations, hl.indices or hl.liq_risk.
kindtransfer, fill, liquidation, index or liq_risk.
tsWhen it happened, UTC, milliseconds: the block time, the fill time, the liquidation's minute or the position capture.
cursorA position within its source. Not the feed's cursor.
chaineth, bsc, base, hyperevm or hyperliquid.
subjectWho or what it is about; keys by kind below.
dataThe facts of the event; keys by kind below.
labelsThe cohorts of subject.wallet when they were looked up: pnl_cohort, pnl_cohort_basis (live), pnl_cum_usd, size_cohort. A missing or null key means unknown, never a cohort. Fill events carry them. A liquidation event carries them only if they were looked up, which a cohort filter on a rule triggers; otherwise the object is empty. Index and liq_risk events carry none.
kindRule typessubjectdata
transfertransferfrom, to, token_address, token_symbol (null for an unmapped token), tx_hashvalue_usd (null when unpriced), priced, block_number, log_index, tx_hash, transfer_type (native or erc20)
fillhl_wallet, hl_cohort_fillwallet, symbolaction (open, close, flip, increase, reduce; null when it could not be worked out, possible only for hl_cohort_fill), side (buy or sell), size_usd, size_token, price, closed_pnl, start_position (signed, before the fill), position_after, crossed (the fill took liquidity), trade_id, dex
liquidationhl_liquidationwallet, symboldirection (long or short: the side of the position that was closed), size_usd, size_token, mark_price, inferred (always true)
indexhl_cohort_biassymbolindex, value, threshold, band, from_side and to_side (high or low), snapshot_ts
liq_riskhl_liq_proximitywallet, symbolasset, direction (long or short), liq_ratio, threshold, band, mark_price, entry_price, liquidation_price, size_usd, leverage, snapshot_ts

Two things to know when you parse them. A fill's trade_id can be larger than 2^53, which JavaScript reads inexactly: take the exact value from event_id (it ends with the id). And a liquidation is inferred from changes between position captures, so its time is the minute it was found, not the instant of the liquidation; see how soon an alert arrives.

Ask your agent

Connected to the Vike MCP server (https://data.vike.io/mcp) in Claude, ChatGPT or Cursor, ask it in plain words. The alerts_events tool reads this feed, at 1 credit a call.

Ask your agent
What have my alerts fired since yesterday?

On this page