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.
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.jsonAlso test these negative cases. Your receiver should answer 401 to each.
| Case | How |
|---|---|
| Bad signature | Change one character of v1 |
| Changed body | Change body.json after signing |
| Old timestamp | Use t=$(( $(date +%s) - 600 )), and sign with that value |
| No header | Remove 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 -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.
PATCHthe expression to something common, such asusd >= 1m and not sampled.PATCHenabledtotrue.- Watch your logs and the delivery log.
PATCHthe 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 state | Likely cause | Fix |
|---|---|---|
pending, attempts: 0 | The worker has not reached it yet | Wait a few seconds |
error: timeout | Your handler took more than 6 seconds | Answer first, then process |
error: connection failed | DNS, TLS or firewall problem at your endpoint | Test with curl -v from outside your network. Check for an expired certificate. |
error: non-2xx reply, code: 401 | Your signature check failed | Run the test vector |
error: non-2xx reply, code: 301 | Your URL redirects, and redirects are not followed | Use the final URL, often with or without a trailing slash |
error: non-2xx reply, code: 403 | A firewall or bot filter blocked it | Allow user agent farbeam-webhooks/1 on that path |
error: host resolves to a private address | DNS for your host returns a private range | Fix DNS |
| Nothing in the log | The rule has no matches, it is off, or it reached the cap | Read the enabled field, then test the expression |
enabled: false, consecutive_failures: 20 | Auto-off | Fix, 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 -s -X DELETE "https://farbeamlabs.com/api/v1/rules/$RULE_ID" \
-H "Authorization: Bearer $FARBEAM_KEY"