Farbeam
Docs / Alerts and webhooks

Testing webhooks

There is no sandbox. Tests use real endpoints, and these tools keep the risk small. The tests below go in order, from cheapest to fullest.

#Test the expression

Validate a rule parses an expression and runs it on the newest 2,000 stored rows. It sends nothing, needs no key and costs 1 unit. You can also use the rule tester.

curl -s -X POST "https://farbeamlabs.com/api/v1/rules/validate" \
  -H "content-type: application/json" \
  -d '{"expression":"usd >= 5m and token in [\"USDC\",\"USDT\"]"}' \
  | jq '.data | {valid, tested, matched}'
const res = await fetch("https://farbeamlabs.com/api/v1/rules/validate", {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({ expression: 'usd >= 5m and token in ["USDC","USDT"]' }),
});
const body = await res.json();
console.log(body.error ?? { tested: body.data.tested, matched: body.data.matched });
import requests

res = requests.post(
    "https://farbeamlabs.com/api/v1/rules/validate",
    json={"expression": 'usd >= 5m and token in ["USDC","USDT"]'},
    timeout=30,
)
body = res.json()
print(body.get("error") or (body["data"]["tested"], body["data"]["matched"]))

If matched is near tested, the rule will reach the hourly cap. If it is 0, check your values, because a typo like chain == "etherium" parses without error.

#Test your signature code offline

Run your verifier against the test vector. This needs no network. It catches most mistakes: a wrong secret encoding, a parsed body, or uppercase hex.

#Test your receiver with a signed request

Sign a request yourself and send it to your receiver. This tests your whole handler, without Farbeam. You can run the receiver on your own machine for this.

curl
export FARBEAM_SECRET='fbs_docs_example_secret_not_real'
cat > body.json <<'JSON'
{"type":"rule.test","rule_id":"rule_local","rule_name":"Local test","sent_at":"2026-11-04T00:00:00.000Z","data":null}
JSON
t=$(date +%s)
v1=$({ printf '%s.' "$t"; cat body.json; } | openssl dgst -sha256 -hmac "$FARBEAM_SECRET" -r | cut -d' ' -f1)
curl -i -X POST "http://LOCAL_RECEIVER/hooks/farbeam" \
  -H "content-type: application/json" \
  -H "x-farbeam-signature: t=$t,v1=$v1" \
  --data-binary @body.json

Also test these negative cases. Your receiver should answer 401 to each.

CaseHow
Bad signatureChange one character of v1
Changed bodyChange body.json after signing
Old timestampUse t=$(( $(date +%s) - 600 )), and sign with that value
No headerRemove the header

Also send a body with a real transfer.matched shape, copied from webhooks, to test your parsing. Recall that data.ts is an integer.

#Test real delivery

Your endpoint must now be public HTTPS on port 443. Farbeam refuses localhost and private addresses, so a local server cannot receive it directly. To reach a machine at home, use your own tunnel or a small public host. Any public HTTPS URL that returns 2xx works for a first check. A test payload holds no private data.

Create a rule with enabled: false, so that no real matches arrive yet, and send a test delivery. Test deliveries work on a disabled rule.

curl -s -X POST "https://farbeamlabs.com/api/v1/rules" \
  -H "Authorization: Bearer $FARBEAM_KEY" \
  -H "content-type: application/json" \
  -d '{"name":"Test","expression":"usd >= 1b","url":"https://example.com/hooks/farbeam","enabled":false}'
curl -s -X POST "https://farbeamlabs.com/api/v1/rules/$RULE_ID/test" \
  -H "Authorization: Bearer $FARBEAM_KEY"

The reply is 202 with {"queued":true,"delivery_id":1}. The limit is 10 per hour per rule. Read the log a few seconds later.

curl
curl -s "https://farbeamlabs.com/api/v1/rules/$RULE_ID/deliveries?limit=1" \
  -H "Authorization: Bearer $FARBEAM_KEY"

#Test with real matches

Turn the rule on with a broad expression for a short time, then tighten it.

  1. PATCH the expression to something common, such as usd >= 1m and not sampled.
  2. PATCH enabled to true.
  3. Watch your logs and the delivery log.
  4. PATCH the expression to its final value.

Rows arrive only while the chain pollers are healthy. Check Status if nothing arrives. Rules do not run on old rows.

#Troubleshooting

Log stateLikely causeFix
pending, attempts: 0The worker has not reached it yetWait a few seconds
error: timeoutYour handler took more than 6 secondsAnswer first, then process
error: connection failedDNS, TLS or firewall problem at your endpointTest with curl -v from outside your network. Check for an expired certificate.
error: non-2xx reply, code: 401Your signature check failedRun the test vector
error: non-2xx reply, code: 301Your URL redirects, and redirects are not followedUse the final URL, often with or without a trailing slash
error: non-2xx reply, code: 403A firewall or bot filter blocked itAllow user agent farbeam-webhooks/1 on that path
error: host resolves to a private addressDNS for your host returns a private rangeFix DNS
Nothing in the logThe rule has no matches, it is off, or it reached the capRead the enabled field, then test the expression
enabled: false, consecutive_failures: 20Auto-offFix, then PATCH enabled: true

Farbeam does not publish a fixed IP list for webhooks, so do not rely on an IP allowlist. Verify the signature.

#Clean up

Delete test rules. They count toward the limit of 10 even when off.

curl
curl -s -X DELETE "https://farbeamlabs.com/api/v1/rules/$RULE_ID" \
  -H "Authorization: Bearer $FARBEAM_KEY"