Feature Request
Motivation
- Svix currently supports filtering by event type (
filterTypes) and channels, but cannot filter by message payload content.
- Many domains require content-aware routing without forking publisher logic per consumer.
- In Web3 specifically, on-chain events often share the same event type but differ by contract address, token ID, chain ID, value thresholds, or allowlists.
Representative Web3 scenarios:
- Deliver only ERC-20 transfers above a threshold:
payload.event == "Transfer" && payload.value > 10_000 * pow(10, payload.decimals)
- Deliver events from specific contracts or chains:
payload.contractAddress == "0xabc..." && payload.chainId == 1
- NFT collection and token range:
payload.event == "Transfer" && payload.contractAddress == "0xNFT..." && payload.tokenId in [1,2,3]
- Stablecoin-only transfers:
payload.symbol in ["USDC", "USDT", "DAI"] && payload.value > 0
- Wallet allowlist or blocklist:
payload.to in ["0xAlice...", "0xBob..."]
This reduces noise and cost for consumers by skipping irrelevant deliveries.
Proposal
- Add an optional
filter field on endpoint create/update that evaluates a boolean expression against the message JSON payload.
- Recommended expression language: Google CEL (Common Expression Language) for safety, performance, and expressiveness (non–Turing-complete, no side effects, widely adopted).
Example request:
{
"url": "https://operational-webhook-destination.com/webhook/",
"filterTypes": ["user.create", "user.updated"],
"filter": "payload.user.age < 18"
}
Server behavior:
- During dispatch, after existing
filterTypes and channels checks, evaluate filter against the payload.
- If
filter is absent, proceed as today.
- If evaluation fails (syntax/type/runtime), fail-closed for that endpoint (skip delivery) and emit logs/operational webhook.
Performance and safety:
- Precompile/cache expressions per endpoint.
- Evaluate only after other pruning to minimize work.
- Restrict accessible variables to
payload and safe stdlib; enforce time/memory limits.
Potential drawbacks:
- Slight dispatch overhead per endpoint due to expression evaluation (mitigated by precompilation and caching).
- Complexity in error handling (decide reject on invalid expressions vs. accept-but-disabled; proposed: reject with validation error).
Alternatives
- Publisher-side filtering: Shifts complexity to producers, often infeasible for multi-tenant or shared pipelines; duplicates logic across clients.
- Overloading
channels as pseudo-filters: Coarse-grained, not expressive for payload content (e.g., numeric thresholds, nested fields).
- Custom DSL: Higher maintenance and security burden vs adopting a well-known, safe, non–Turing-complete language like CEL.
- Consumer-side filtering: Increases delivery volume, wastes bandwidth and compute, adds latency and cost.
CEL was chosen for:
- Strong safety guarantees, no loops/recursion, no side effects.
- Good performance with precompilation and a small runtime surface.
- Sufficient expressiveness for Web3 and general content-based routing.
Feature Request
Motivation
filterTypes) andchannels, but cannot filter by message payload content.Representative Web3 scenarios:
payload.event == "Transfer" && payload.value > 10_000 * pow(10, payload.decimals)payload.contractAddress == "0xabc..." && payload.chainId == 1payload.event == "Transfer" && payload.contractAddress == "0xNFT..." && payload.tokenId in [1,2,3]payload.symbol in ["USDC", "USDT", "DAI"] && payload.value > 0payload.to in ["0xAlice...", "0xBob..."]This reduces noise and cost for consumers by skipping irrelevant deliveries.
Proposal
filterfield on endpoint create/update that evaluates a boolean expression against the message JSON payload.Example request:
{ "url": "https://operational-webhook-destination.com/webhook/", "filterTypes": ["user.create", "user.updated"], "filter": "payload.user.age < 18" }Server behavior:
filterTypesandchannelschecks, evaluatefilteragainst the payload.filteris absent, proceed as today.Performance and safety:
payloadand safe stdlib; enforce time/memory limits.Potential drawbacks:
Alternatives
channelsas pseudo-filters: Coarse-grained, not expressive for payload content (e.g., numeric thresholds, nested fields).CEL was chosen for: