Farbeam
Docs / Alerts and webhooks

Beamline

Beamline is a small expression language for alert rules. An expression reads one transfer row and returns true or false. It has no loops, variables, functions or state, and the parser compiles it to a closure without calling eval.

Beamline
usd >= 5m and token in ["USDC", "USDT"] and to_type == "exchange"

Test an expression with Validate a rule or the rule tester. See fields and examples.

#Grammar

Text
expression  = or_expr
or_expr     = and_expr { "or" and_expr }
and_expr    = not_expr { "and" not_expr }
not_expr    = "not" not_expr | atom
atom        = "(" or_expr ")"
            | bool_field
            | bool_field ( "==" | "!=" ) ( "true" | "false" )
            | number_field compare number
            | number_field [ "not" ] "in" "[" number { "," number } [ "," ] "]"
            | text_field text_op string
            | text_field [ "not" ] "in" "[" string { "," string } [ "," ] "]"
compare     = "==" | "!=" | ">" | ">=" | "<" | "<="
text_op     = "==" | "!=" | "contains" | "startswith"
number      = digit { digit | "_" } [ "." digit { digit } ] [ "k" | "m" | "b" ]
string      = '"' { any character } '"' | "'" { any character } "'"

Whitespace between tokens is optional. There are no comments.

#Types and fields

TypeFields
Textchain, token, token_address, kind, from, to, from_label, to_label, from_type, to_type, tx
Numberusd, amount, block
Booleansampled

id, uid and ts are in the payload, and they are not rule fields.

#Operators

OperatorTextNumberBooleanNotes
==, !=YesYesYes, with true or false
>, >=, <, <=NoYesNo
containsYesNoNoSubstring
startswithYesNoNoPrefix
in, not inYesYesNoList of literals
and, or, notWorks on conditions

#Rules of comparison

  • Text comparison ignores case. token == "usdc" matches USDC. A checksummed address matches a lowercase from. This also holds for Solana base58, so two addresses that differ only by case would compare equal.
  • **null becomes an empty string.** from_label == "" is true for a row with no label, and from_label != "" means it has one. token_address == "" is true for native rows.
  • **true and false must be lowercase.** All other keywords and field names ignore case. USD >= 1M AND TOKEN == "usdc" is valid.
  • Values are not checked against known values. chain == "etherium" parses and never matches. Run the validate call and look at matched.

#Numbers

FormValue
10000001,000,000
1_000_0001,000,000
250k or 250K250,000
5m or 5M5,000,000
2.5m2,500,000
1b or 1B1,000,000,000

Numbers cannot be negative and cannot use exponent form. Decimals need a digit before the dot. .5 and 1e6 are errors.

usd and amount are doubles, so == on them almost never matches. Use a range.

#Strings

Use double or single quotes. A backslash makes the next character literal. For example, "a\"b" is the three characters a"b. Field values rarely need this.

#Precedence

From tightest to loosest: not, then and, then or. Use parentheses when you mix them.

ExpressionReads as
a or b and ca or (b and c)
not a and b(not a) and b
not (a or b)Neither a nor b
Beamline
(token == "USDC" or token == "USDT") and usd >= 1m

Without the parentheses, that rule would match every USDC row of any size above the floor, plus USDT rows of $1m or more.

#Boolean shorthand

sampled alone is true for Solana rows. not sampled excludes them. sampled == false and sampled != true give the same result.

#Errors

A rule that does not parse returns 422 invalid_rule. POST /rules/validate and POST /rules return the same errors. Most messages end with (at position N), a zero-based character offset into your string. Each row below was tested on 2026-11-04 UTC.

InputMessage
Empty or spacesRule is empty
1,001 charactersRule is longer than 1000 characters
More than 64 conditionsRule has more than 64 conditions
foo == 1Unknown field "foo" (at position 0)
usdExpected an operator after "usd" (at position 3)
usd >Expected a value after ">" (at position 5)
usd == "x""usd" needs a number (at position 7)
token > "a"">" works only on number fields (at position 6)
token > 5"token" needs a quoted string (at position 8)
token == USDC"token" needs a quoted string (at position 9)
usd contains 5"contains" works only on text fields (at position 4)
usd "x"Unknown operator "x" (at position 4)
usd and 5Unknown operator "and" (at position 4)
usd ~ 5Unexpected character "~" (at position 4)
usd > -1Unexpected character "-" (at position 6)
usd > 1e6Unexpected character "1" (at position 6)
(usd > 1Missing ")" (at position 8)
usd > 1)Unexpected ")" (at position 7)
()Expected a field name (at position 1)
usd > 1 andRule ends too early (at position 11)
"abcUnterminated string (at position 0)
chain == "a" "b"Unexpected "b" (at position 13)
token in []List is empty (at position 6)
token in "a"Expected "[" after "in" (at position 9)
token in ["a"Expected "," or "]" (at position 13)
token in [1]List values for "token" must be strings (at position 10)
usd in ["a"]List values for "usd" must be numbers (at position 8)
from_label not contains "x"Expected "in" after "not" (at position 15)
sampled == 1Expected true or false (at position 11)
sampled == TrueExpected true or false (at position 11)
sampled not in ["a"]Unexpected "not" (at position 8)

To negate contains, put not in front of the whole condition: not from_label contains "x".

#Limits

#How rules run

Each enabled rule is compiled when it is saved or when the service starts. A new row runs through all enabled transfer rules. A rule that names liquidity_usd or change_pct runs on liquidity samples instead. After a rule fires, it ignores matches until its cooldown ends. Rules read only the row itself, so a rule cannot refer to earlier rows, sums, counts or time windows. To alert on "more than $10m in an hour", receive the rows in your own service and keep the count there.