The fastest way I’ve found to keep an eye on REST and GraphQL endpoints is to turn OpenClaw into a lightweight SRE assistant. Because the framework already speaks cron, shell, and webhooks, you can bolt together a full health-check pipeline without pulling in another black-box SaaS. This post walks through exactly how I wired OpenClaw (v4.3.1, Node 22.2) to:
- Call a list of endpoints every minute and log status + latency
- Persist metrics to disk (or DynamoDB if you prefer)
- Alert through PagerDuty or Opsgenie webhooks on failures & degradation
- Push updates to a public Statuspage-like JSON feed
- Kick off automated remediation scripts when an outage is confirmed
Why OpenClaw instead of yet another monitoring tool?
I used to run a mix of Pingdom, StatusCake, and hand-rolled cron jobs. They worked, but:
- Latency data lived in different dashboards.
- Incident rules were limited to what the vendor exposed.
- Shell-level automation (e.g. restarting a container) required a sidecar runner.
OpenClaw already gives me browser control, shell access, scheduled tasks, and a persistent memory store. Toss in an HTTP request helper and you’ve got a web monitor that runs alongside the rest of your agents in ClawCloud — or locally if you’re paranoid.
Prerequisites and quick install
You need Node 22 or newer. I’m on 22.2.1. Locally:
$ npm install -g openclaw@4.3.1
$ openclaw gateway init api-monitor
If you’d rather not host, open claw.cloud/new, pick a region, name the agent, and you’re live in ~60 seconds. The rest of the tutorial works the same way; you’ll just paste code snippets into the ClawCloud web editor.
Defining the monitor job
Create claws/healthMonitor.js inside your agent directory:
import fetch from "node-fetch";
export default async function ({ tools, memory, args }) {
const targets = [
{ name: "Users API", url: "https://api.example.com/v1/users" },
{ name: "GraphQL", url: "https://api.example.com/graphql" },
{ name: "Auth", url: "https://auth.example.com/login" }
];
const now = Date.now();
const results = [];
for (const t of targets) {
const start = performance.now();
let ok = false, status = 0, error = null;
try {
const res = await fetch(t.url, { method: "GET", timeout: 10000 });
status = res.status;
ok = res.ok;
} catch (e) {
error = e.message;
}
const ms = Math.round(performance.now() - start);
results.push({
name: t.name,
ok,
status,
ms,
error,
ts: now
});
}
// Persist to agent memory (a tiny SQLite under the hood)
await memory.push("api_metrics", results);
return results;
}
This returns an array of checks and sticks them into persistent memory. The memory.push helper appends rows to a logical table (api_metrics). If you need long-term retention, switch the storage driver to Postgres or DynamoDB in openclaw.config.json:
{
"memory": {
"driver": "dynamodb",
"table": "api-metrics"
}
}
Scheduling with cron inside OpenClaw
Add a task in daemon.yaml (the file the OpenClaw daemon watches):
cron:
- name: api-health
schedule: "*/1 * * * *" # every minute
claw: ./claws/healthMonitor.js
retries: 0
I like to keep retries: 0 here because I want the raw result; retries can hide brownouts. The gateway UI will show the last run and duration.
Detecting failures and latency spikes
Polling is only half the story. We need something that decides when conditions warrant an incident. I added another claw, claws/evaluateHealth.js:
export default async function ({ memory, tools }) {
const lastFive = await memory.query(
"SELECT * FROM api_metrics ORDER BY ts DESC LIMIT 15"
);
const grouped = Object.groupBy(lastFive, r => r.name);
const alerts = [];
for (const [name, rows] of Object.entries(grouped)) {
const failures = rows.filter(r => !r.ok);
const slow = rows.filter(r => r.ms > 1000); // 1s threshold
if (failures.length >= 3) {
alerts.push({
name,
type: "down",
msg: `${name} is down (${failures.length} / 5 recent checks failed)`
});
} else if (slow.length >= 5) {
alerts.push({
name,
type: "slow",
msg: `${name} latency >1s on 5 consecutive checks`
});
}
}
return alerts;
}
We’ll schedule this every minute right after the healthMonitor:
cron:
- name: api-health
schedule: "*/1 * * * *"
claw: ./claws/healthMonitor.js
retries: 0
- name: api-evaluate
schedule: "*/1 * * * *"
claw: ./claws/evaluateHealth.js
retries: 0
Alerting with PagerDuty and Opsgenie webhooks
PagerDuty, Opsgenie, and even plain Slack all understand a simple POST payload. OpenClaw ships with the tools.http.post helper, but you can use fetch just the same. Create claws/dispatchAlert.js:
const PD_ENDPOINT = process.env.PAGERDUTY_WEBHOOK;
const OG_ENDPOINT = process.env.OPSGENIE_WEBHOOK;
const SLACK_WEBHOOK = process.env.SLACK_WEBHOOK;
export default async function ({ args }) {
const { alerts } = args;
if (!alerts.length) return "no-ops";
const body = {
summary: "API Monitor Alert",
source: "openclaw",
severity: alerts.some(a => a.type === "down") ? "critical" : "warning",
custom_details: alerts
};
const payload = JSON.stringify(body);
const headers = { "Content-Type": "application/json" };
if (PD_ENDPOINT) await fetch(PD_ENDPOINT, { method: "POST", headers, body: payload });
if (OG_ENDPOINT) await fetch(OG_ENDPOINT, { method: "POST", headers, body: payload });
if (SLACK_WEBHOOK) await fetch(SLACK_WEBHOOK, { method: "POST", headers, body: JSON.stringify({ text: body.summary, attachments: alerts }) });
return `sent ${alerts.length} alerts`;
}
Wire it up by modifying evaluateHealth.js to forward results via the built-in tools.run helper:
if (alerts.length) {
await tools.run("./claws/dispatchAlert.js", { alerts });
}
Publishing a JSON status page
If you want a public-facing heartbeat, expose a tiny route through the gateway’s express server. Add to gateway.js (or use the Routes panel in ClawCloud):
export default ({ express, memory }) => {
const router = express.Router();
router.get("/status.json", async (req, res) => {
const latest = await memory.query(
"SELECT * FROM api_metrics ORDER BY ts DESC LIMIT 30"
);
const grouped = Object.groupBy(latest, r => r.name);
const summary = {};
for (const [name, rows] of Object.entries(grouped)) {
summary[name] = {
ok: rows[0].ok,
status: rows[0].status,
ms: rows[0].ms,
checked_at: rows[0].ts
};
}
res.json({ generated_at: Date.now(), services: summary });
});
return router;
};
Deploy the gateway and you’ll get /status.json for free. I point a simple static site at that endpoint and render green/red indicators with 20 lines of Alpine.js.
Automated incident response: restart containers & open tickets
Because OpenClaw can run shell commands, you can tie remediation directly to the alert flow. Below is a stripped-down example using Docker:
export default async function ({ args, tools, shell }) {
const { alerts } = args;
for (const a of alerts) {
if (a.name === "Users API" && a.type === "down") {
// quick restart – assumes same host running agent
await shell.exec(`docker restart users-api`);
await tools.http.post(process.env.SLACK_WEBHOOK, {
text: "Users API container restarted by OpenClaw"
});
}
}
}
You could just as easily call kubectl rollout restart, hit the AWS SDK, or create a GitHub incident issue through Composio’s GitHub integration.
Local vs. ClawCloud: latency & reliability trade-offs
I ran the same monitor both on my laptop and in ClawCloud (US-EAST-1). Here’s what I saw over a week:
- Local agent: 12% of checks showed inflated latency when my VPN kicked in. Also missed 3 intervals when the laptop slept.
- ClawCloud agent: Consistent 15–20 ms RTT to AWS us-east-1 targets, 0 missed cron executions.
If you need the “outside world” perspective, spin up a second agent in another region and aggregate both. Memory drivers can point to the same DynamoDB table so you maintain a unified status feed.
Cost math (what this actually runs you)
- ClawCloud Free tier: 3 agents, 1 vCPU each. More than enough for 100 endpoints at 30 s intervals (CPU sits at 2–3%).
- PagerDuty: Integrations via webhooks are free on the Starter plan.
- Opsgenie: Same deal — incoming webhooks don’t bump you to a higher tier.
- Storage: 30 days of metrics at 1-minute resolution ≈ 130 k rows. DynamoDB on on-demand hits maybe $0.40/month.
Cheaper than most commercial API monitors, and you keep code ownership.
Hard edges you’ll probably hit
- Cron second-level granularity. OpenClaw’s daemon uses
node-cron; the smallest unit is one minute. If you need sub-minute bins, loop inside the claw or run multiple agents with offset schedules. - Cold starts. On serverless ClawCloud free tier, agents sleep after 30 m idle. The first cron after sleep adds ~1 s. If that skews latency, add an every-5-minute warm-up no-op task.
- PagerDuty deduping. They dedupe by
incident_key. Include a deterministic key so flapping checks don’t spawn new incidents each minute. - Memory table size. SQLite driver slows down past ~1 M rows. Rotate or archive to S3 once a day, or use an external DB from the start.
What to try next
- Add synthetic transactions with the built-in browser tool: log in, create an order, validate redirect.
- Feed metrics to Prometheus via a
/metricsroute and scrape with Grafana Cloud. - Use Composio to automatically open a Notion post-mortem doc when an incident closes.
The pieces above took about two hours to wire up end-to-end, most of it tweaking thresholds. And the code lives with the rest of our infra repo, which means pull requests, code review, and predictable diffs instead of clicking around yet another dashboard.
If you build on this, drop a note in the GitHub discussions — always curious how others are bending OpenClaw to their will.