Rendered from the source repository. Headings, examples, code, tables, links, and referenced images are preserved.
Oodle Synthetic Monitors — HTTP / TCP Checks
This skill teaches the agent to configure synthetic monitors that actually catch failures: assertions, sane intervals, and update-without-overwrite.
Prerequisites
brew install oodle-ai/oodle/oodle
oodle configureConfirm the synthetic-monitors endpoint works:
oodle synthetic-monitors list -o json | jq 'length'Command Execution Order
Before running any oodle command:
- Check whether the target URL or hostport is already in context.
- If not, ask the user to confirm the target.
- Test the target manually (
curl -I <url>) to confirm it is reachable from the public internet. - Run
oodle synthetic-monitors create -f monitor.json. - After creation, verify the first run succeeded with
oodle synthetic-monitors get <id> -o json.
Quick Reference
| Task | Command |
|---|---|
| List monitors | oodle synthetic-monitors list -o json |
| Get monitor | oodle synthetic-monitors get <id> -o json |
| Create monitor | oodle synthetic-monitors create -f monitor.json |
| Update monitor | oodle synthetic-monitors update <id> -f monitor.json |
| Delete monitor | oodle synthetic-monitors delete <id> --force |
Common Operations
Synthetic monitor schema (HTTP)
{
"name": "API health check",
"type": "http",
"target": "https://api.example.com/health",
"interval": 60,
"assertions": [
{"type": "statusCode", "value": "200"},
{"type": "responseTime", "value": "2000"}
]
}| Field | Meaning |
|---|---|
type | http or tcp |
target | URL (HTTP) or host:port (TCP) |
interval | Seconds between probes; minimum 30, recommended ≥ 60 |
assertions[].type | statusCode, responseTime, bodyContains, headerEquals |
assertions[].value | String form of the expected value (e.g. "200", "2000", "ok") |
Synthetic monitor schema (TCP)
{
"name": "Postgres reachability",
"type": "tcp",
"target": "postgres.example.com:5432",
"interval": 60,
"assertions": [
{"type": "responseTime", "value": "1000"}
]
}Creating a monitor
# ✅ CORRECT — manual reachability test, then create
curl -sSfI https://api.example.com/health
oodle synthetic-monitors create -f monitor.json
# ❌ WRONG — creating against an unreachable target leaves a permanently-failing monitor
oodle synthetic-monitors create -f monitor.jsonUpdating a monitor
# ✅ CORRECT — get → edit → update preserves all assertions
oodle synthetic-monitors get syn_123 -o json > monitor.json
jq '.interval = 30' monitor.json > monitor.new.json
oodle synthetic-monitors update syn_123 -f monitor.new.json
# ❌ WRONG — partial payload removes assertions
oodle synthetic-monitors update syn_123 -f <(echo '{"interval":30}')Deleting a monitor
# ✅ CORRECT
oodle synthetic-monitors get syn_123 -o json > /dev/null
oodle synthetic-monitors delete syn_123 --force
# ❌ WRONG — name-grep delete
oodle synthetic-monitors delete "$(oodle synthetic-monitors list | grep health | awk '{print $1}')" --forceBest Practices
Use interval: 60 (or higher) for non-critical endpoints
A 10-second interval on every endpoint multiplies cost and noise. Reserve short intervals for critical user-facing flows.
# ✅ CORRECT — 60s interval for a /health endpoint
"interval": 60
# ❌ WRONG — 10s on dozens of internal endpoints
"interval": 10Always include at least one statusCode assertion
A monitor with no assertions only checks TCP reachability — it will pass even if the app returns 500 to every request.
# ✅ CORRECT
"assertions": [{"type":"statusCode","value":"200"},{"type":"responseTime","value":"2000"}]
# ❌ WRONG — monitor "succeeds" on a 500 response
"assertions": []Add a responseTime assertion to catch slow dependencies
A 200 response that takes 30 seconds is still a failure for users.
# ✅ CORRECT
"assertions": [
{"type":"statusCode","value":"200"},
{"type":"responseTime","value":"2000"}
]
# ❌ WRONG — only checks status code, doesn't catch latency regressions
"assertions": [{"type":"statusCode","value":"200"}]Always get before update to preserve assertions
Update is a full-document replace.
# ✅ CORRECT
oodle synthetic-monitors get syn_123 -o json > m.json
jq '.assertions += [{"type":"bodyContains","value":"ok"}]' m.json > m.new.json
oodle synthetic-monitors update syn_123 -f m.new.json
# ❌ WRONG — drops `assertions` and `target`
oodle synthetic-monitors update syn_123 -f <(echo '{"interval":120}')Use a stable name per environment
Encode env in the name (API health check (prod), API health check (staging)) so the alert recipient knows which env to investigate.
# ✅ CORRECT
"name": "API health check (prod)"
# ❌ WRONG
"name": "health"Failure Handling
| Error | Cause | Fix |
|---|---|---|
| 401 Unauthorized | Invalid or missing API key | Run oodle configure or set OODLE_API_KEY |
| 404 Not Found | Synthetic monitor ID does not exist | Verify with oodle synthetic-monitors list -o json |
| connection refused | Wrong OODLE_DEPLOYMENT URL | Check OODLE_DEPLOYMENT env var |
| Monitor permanently red | Target unreachable from the probe egress | curl -I from the public internet; check firewall, DNS, TLS cert |
| Monitor flaps | interval too aggressive or responseTime assertion too tight | Raise interval to 60s; raise responseTime to p95+headroom |
| Assertions silently dropped | update was called with a partial payload | Re-create from the last get snapshot; always get → edit → update |
| 429 Too Many Requests | Many probes scheduled at the same second | Stagger by editing interval per monitor; use --retries 3 for bulk operations |

