Skip to content

Feature Request: Payload-level filtering when creating endpoints (e.g., CEL expressions) #2058

@torbensen

Description

@torbensen

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions