Skip to main content
A rule points one endpoint at one event type and carries your own configuration. The platform declares what each event can be scoped and conditioned on; you choose the values. The model is the one you know from spreadsheet conditional formatting: a range, then rules.
A rule with no filters matches every event of its type. Every venue and symbol the event covers, every watched address for an account.* event, and no conditions, with each parameter at its declared default. On a market-wide event that can be a lot of deliveries, so narrow it here and check the rate with the estimate before you create it.

A first rule

Every Hyperliquid BTC liquidation of $250,000 or more:
The configuration goes in filters (config is accepted as the same key) and has three parts:
  • Scope keys (venue, symbols, and addresses on account events) choose where the event is watched.
  • conditions compare the event’s declared metrics with your values, here notional_usd at least 250,000.
  • params define what an occurrence is, such as a burst’s window. This rule sets none, so max_age_s takes its default.
Every key is checked against the event’s declaration in the catalog. A filter the event does not accept, an undeclared metric or param, an operator that does not fit the metric’s type, an out-of-range value or an unknown enum member is refused with a 400 that names what is allowed. Scope keys, conditions or params sent at the top level of the body, outside filters, are refused too, rather than creating a rule without them.

Scope

  • venue (string or list of strings): restrict to venues the event covers. Absent means every venue it covers.
  • symbols (list of strings): restrict to these markets, in venue-native form. Absent means every market.
  • addresses (list of strings, account.* events only): a subset of your watched addresses. Absent means all of them. Each one must already be on your watched list.

Symbol conventions

Symbols are venue-native. A symbol written in the wrong form matches nothing.
  • Hyperliquid perps: the bare coin, BTC.
  • HIP-3: dex prefix and coin, xyz:AAPL. A bare AAPL matches nothing.
  • Hyperliquid Spot: the dashed pair, HYPE-USDC.
  • HIP-4 outcome sides: # and the asset id, #20481.
  • Lighter (lighter): the bare market symbol, BTC.
  • Lighter on Robinhood Chain (rh-lighter): the bare perp symbol, BTC, or the dashed spot pair with the USDG quote, AAPL-USDG.
  • account.transfer and market.pga_payment: the token, not a pair, such as USDC or HYPE.

Conditions

Conditions compare a metric the event declares with a value. A rule takes up to 16 conditions, and all of them must hold. No conditions means every occurrence: account.fill with no conditions is every fill of a watched address, and market.liquidation with no conditions is every liquidation on the covered venues from the 100 USD scan floor up.
  • Numbers: greater_than, greater_than_or_equal, less_than, less_than_or_equal, equal, not_equal, between, not_between, in, not_in.
  • Text and enumerated values: equal, not_equal, in, not_in, contains, not_contains, starts_with, ends_with.
  • True or false values: equal, not_equal.
  • Timestamps: before, after, equal, with RFC 3339 values.
  • Any type: is_empty, is_not_empty.
Symbol spellings (>=, <, != and the like) are accepted and stored in word form. between and not_between take [low, high]; in and not_in take a non-empty list; is_empty and is_not_empty take no value. A custom-formula operator (formula) is reserved and refused for now; combine conditions instead. min_notional_usd at the top level of filters is shorthand for a notional_usd greater_than_or_equal condition, or usdc_value on account.transfer, the one event whose notional metric has that name. It is refused on events that declare no notional metric. Which metrics an event declares, with their types, units and enum values, is in the catalog: GET /v1/webhooks/event-types returns metrics, operators and params for each event.

Relative values

Four metrics let a threshold mean the same thing on a large market and a small one. They are conditionable like any other metric.
  • notional_pct_volume_1h and notional_pct_volume_24h: the event’s notional as a percent of that market’s trailing volume. On market.liquidation, account.liquidated, account.fill and market.liquidation_burst.
  • notional_pct_oi: as a percent of that market’s open interest. On the same events, perps only.
  • notional_pct_wallet_volume_30d: as a percent of that wallet’s own 30-day volume. On account.fill.
That is “a liquidation worth at least 2 percent of this market’s last hour of volume”, on every market at once. Market references are refreshed every minute and wallet volumes every 10 minutes. A relative value is null, so a condition on it does not fire, when its denominator is under 10,000 USD, which keeps a thin market from producing a huge percentage. notional_pct_oi is null on spot markets, and the volume percentages are null on Lighter on Robinhood Chain. A payload with a relative value carries reference_at, the time the denominator was measured.

Params

Some events are defined by a window or a level, so the value changes what an occurrence is rather than filtering one. This is the full list; defaults are in parentheses.
  • market.liquidation_burst: window_s one of 60, 300, 900, 3600 (300); threshold_mode one of usd, pct_oi, pct_volume_1h (usd); threshold_usd from 1,000, used in usd mode (1,000,000); threshold_pct 0.01 to 100, used in the two percent modes (1).
  • market.oi_delta: window_s one of 300, 900, 3600 (900); threshold_pct 0.1 to 1000 (10).
  • market.breadth_cross: threshold 1 to 99 percent (30); hysteresis_pct 0 to 50 points (5).
  • oracle.jump: threshold_pct 0.1 to 50 (2).
  • oracle.stall: window_s one of 60, 120, 300, 900, 3600 (300).
  • ingest.stall and ingest.recovered: threshold_s one of 60, 120, 300, 600, 1800, 3600 (600). A market is judged against it only when the threshold is at least five times its usual update interval. Lighter books update about once a minute, so on the Lighter venues most markets are judged from 600 seconds and the sparsest only from 1800.
  • chain.block_stall: threshold_s one of 10, 20, 30, 60, 120, 300, 600 (20).
  • chain.block_time_degraded: degraded_pct one of 10, 20, 30, 50 (20); consecutive_minutes one of 1, 2, 3, 5 (2).
  • billing.credit_low: levels_pct, a list of percent-remaining levels, each 0 to 100 ([25, 10, 0]).
  • market.liquidation, market.pga_payment, hip4.settlement and every account.* event: max_age_s 60 to 86400 (3600, or 7200 for hip4.settlement and account.hip4_settled). See below.
market.liquidation_burst can be written in relative terms: {"params": {"window_s": 60, "threshold_mode": "pct_oi", "threshold_pct": 1}} is “liquidations inside a minute worth one percent of that market’s open interest”, a meaningful threshold on every perp without picking a dollar figure per market. A market without a reference does not fire the percent modes. The default mode is usd, so a rule written before modes existed keeps firing exactly as it did. Omitted params take the declared default. A value outside the range or off the menu is refused with a 400 naming the allowed values. A declared param may also be written at the top level of filters ("threshold_s": 60) and is routed into params; on market.liquidation_burst, a top-level threshold means threshold_usd. Two rules with different params are two different triggers, and their deliveries carry different event ids.

max_age_s and late deliveries

max_age_s is the oldest occurrence you still want delivered, in seconds behind real time. In normal operation it never comes into play. After an outage on our side, the engine catches up as far back as your max_age_s and delivers those occurrences with late_ms set, and with late: true when they are more than 10 minutes behind, instead of dropping them. Anything older is skipped; fetch it from the REST API or an export. Set it low when a stale alert is worse than no alert, such as a trading trigger, and high when completeness matters more, such as an audit trail.

What the API stores

A fuller rule, on fills by a watched address:
The response echoes the stored configuration, normalized: operators in their word form, addresses lowercased, every declared param present with defaults filled in, and min_notional_usd mirrored from the loosest notional_usd lower bound among your conditions.

Edit or switch off a rule

PATCH /v1/webhooks/subscriptions/{id} with {"config": {...}} replaces the whole configuration in place, validated exactly like create (filters is accepted as the key too). {"enabled": false} switches delivery off without losing the configuration, and {"enabled": true} switches it back on, so tuning a threshold never means deleting and recreating the rule. The response is the updated rule in the same shape as create.
This raises the fill rule above from 25,000 to 50,000 and keeps everything else. Because the new configuration replaces the old one, send every key you want to keep. A key you leave out returns to its default: without addresses the rule covers every watched address, without the side condition it covers both sides, and without params its max_age_s returns to 3600. enabled is your switch, and it is separate from the engine’s own pause when a rule reaches the plan’s daily allowance. Resuming a paused rule never flips your switch, and switching a rule on never resumes it. See Paused rules. DELETE /v1/webhooks/subscriptions/{id} removes a rule for good.

Next step

See how often the rule would fire

Replay the configuration over recent history before you create it, and pick a threshold from the rate you want.
Last modified on October 6, 2026