How To

DIY Cloudflare Automation: API Tokens, Notifications, Health Checks

A hands-on guide to Cloudflare automation using only native tools — part two of our Cloudflare series. Set up scoped API tokens as your security baseline, then automate DNS records, WAF custom rules, and cache purges with exact curl calls against the v4 API. Wire Notification webhooks for WAF spikes, certificate expiry, and origin health, add multi-region health checks and load balancer monitors, and schedule weekly audit-log reviews to catch config drift. Closes with the honest ceiling of DIY: the API executes decisions you already made — it does not investigate or decide.

·
cloudflareautomationsecuritycloudflareapiwafdnscdn
Cover Image for DIY Cloudflare Automation: API Tokens, Notifications, Health Checks

DIY Cloudflare Automation: API Tokens, Notifications, Health Checks

A scoped API token and three notification webhooks will carry you further than most teams expect. Before you evaluate any third-party tooling, it's worth knowing exactly how much Cloudflare automation you can build with what's already in the product — because the honest answer is "quite a lot," and the equally honest follow-up is "until you're maintaining a pile of cron jobs and shell scripts that nobody remembers writing."

In part one of this series we mapped the config-sprawl problem: zones accumulating years of page rules and WAF exceptions, DNS records without owners, settings drifting between zones, and security events nobody reviews. This article is the DIY response — automating detection and routine action with Cloudflare's native features only: the API, scoped tokens, Notifications, health checks, and audit logs. Every command below runs against api.cloudflare.com/client/v4 and is copy-pasteable today.

Step 0: Scoped API tokens — the security baseline

Everything else in this article depends on API access, so get the credential model right first. Cloudflare has two options: the legacy Global API Key (full access to everything in your account, forever) and API tokens (scoped to specific permissions, specific zones, optional IP restrictions, optional expiry). There is no defensible reason to use the Global API Key for automation in 2026. Create a token instead: dash.cloudflare.com → My Profile → API Tokens → Create Token.

Two rules that will save you an incident later:

  • One token per job. Your DNS sync script gets Zone → DNS → Edit on one zone. Your audit script gets read-only permissions across zones. When a token leaks, the blast radius is one job, not your entire edge.
  • Read-only by default. Start every token with Read permissions and add Edit only when a script actually writes.

Verify a token before wiring it into anything:

curl "https://api.cloudflare.com/client/v4/user/tokens/verify" \
  -H "Authorization: Bearer $CF_API_TOKEN"

A healthy response returns "status": "active". From here on, $CF_API_TOKEN, $ZONE_ID, and $ACCOUNT_ID are assumed to be set in your shell. Zone IDs are on each zone's Overview page, or via the API:

curl "https://api.cloudflare.com/client/v4/zones?per_page=50" \
  -H "Authorization: Bearer $CF_API_TOKEN" \
  | jq -r '.result[] | "\(.id)  \(.name)"'

Tool 1: The API — DNS, WAF rules, and cache as code

The Cloudflare API covers essentially everything the dashboard does. Three areas account for most of the routine operations work.

DNS records

Start with an inventory — this is the export you wished you had in part one when we asked "who owns this record?":

curl "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records?per_page=100" \
  -H "Authorization: Bearer $CF_API_TOKEN" \
  | jq -r '.result[] | [.type, .name, .content, (.proxied|tostring)] | @tsv'

Dump this to a file nightly and diff against yesterday's — that's a functioning DNS change detector in four lines of cron. Creating a record is a single POST ("ttl": 1 means Auto):

curl -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records" \
  -H "Authorization: Bearer $CF_API_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{"type":"A","name":"api.example.com","content":"203.0.113.10","ttl":1,"proxied":true}'

Updates use PATCH .../dns_records/$RECORD_ID with the same body shape. The classic automation here: a failover script that repoints an A record when your primary origin dies. It works — but note it's you who decided the origin was dead, on whatever logic your script implements at 3 a.m.

WAF custom rules

WAF custom rules live in the Rulesets API under the http_request_firewall_custom phase. First, list what you already have — most zones carry rules nobody can explain:

curl "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/rulesets/phases/http_request_firewall_custom/entrypoint" \
  -H "Authorization: Bearer $CF_API_TOKEN" \
  | jq '.result.rules[] | {id, description, action, expression}'

Adding a rule is a POST to that ruleset. Prefer managed_challenge over block for anything you're not certain about — it filters bots without hard-failing real users:

curl -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/rulesets/$RULESET_ID/rules" \
  -H "Authorization: Bearer $CF_API_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{
    "action": "managed_challenge",
    "expression": "(http.request.uri.path contains \"/wp-login.php\")",
    "description": "Challenge WordPress login probes"
  }'

The same entrypoint listing, run on every zone and diffed, is your WAF drift detector: when the staging zone's rules no longer match production's, you'll know before an attacker does.

Cache purge

Purging by URL after a deploy is the single most common Cloudflare automation, and it's one call:

curl -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/purge_cache" \
  -H "Authorization: Bearer $CF_API_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{"files":["https://example.com/assets/app.js","https://example.com/index.html"]}'

You can also purge by prefix, tag, or host (Enterprise for tags/hosts), or send {"purge_everything":true} — which you should treat like rm -rf: it works instantly and your origin absorbs the resulting stampede. Wire the targeted version into CI as a post-deploy step and one recurring category of "why is the old version still live" tickets disappears.

Tool 2: Notifications — webhooks for the signals that matter

Part one identified the signals worth watching: WAF spikes, certificate expiry, origin health. Cloudflare Notifications can push all three to a webhook — Slack, PagerDuty, or your own endpoint. Console path: dash.cloudflare.com → Manage Account → Notifications.

Register the webhook destination first:

curl -X POST "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/alerting/v3/destinations/webhooks" \
  -H "Authorization: Bearer $CF_API_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{"name":"ops-webhook","url":"https://hooks.example.com/cloudflare"}'

Then create policies against it. The three that pay for themselves immediately:

Notification (dashboard name) Alert type (API) What it catches
Security Events Alert clickhouse_alert_fw_anomaly Spikes in WAF/firewall event volume
Universal SSL Alert universal_ssl_event_type Certificate validation/expiry problems
Health Check Status Notification health_check_status_notification Origin flipping unhealthy
curl -X POST "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/alerting/v3/policies" \
  -H "Authorization: Bearer $CF_API_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{
    "name": "WAF spike to ops webhook",
    "enabled": true,
    "alert_type": "clickhouse_alert_fw_anomaly",
    "mechanisms": {"webhooks": [{"id": "$WEBHOOK_ID"}]},
    "filters": {"zones": ["$ZONE_ID"]}
  }'

One copy-paste caveat: $WEBHOOK_ID (returned by the destinations call above) and $ZONE_ID sit inside a single-quoted JSON body, so your shell will not expand them — substitute the real values before running.

Note what a notification is: a fact, delivered. "Firewall events spiked on zone X" arrives in Slack with no indication of whether it's a credential-stuffing run, a scraper, or your own load test — that classification work is still yours, every time the webhook fires.

Tool 3: Health checks and load balancer monitors

Standalone Health Checks (Pro plan and up) probe your origin from multiple regions and feed the health-status notification above:

curl -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/healthchecks" \
  -H "Authorization: Bearer $CF_API_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{
    "name": "origin-health",
    "address": "origin.example.com",
    "type": "HTTPS",
    "check_regions": ["WNAM", "ENAM", "WEU"],
    "http_config": {"method": "GET", "path": "/healthz", "expected_codes": ["200"]},
    "interval": 60,
    "retries": 2,
    "timeout": 5
  }'

Set retries to at least 2 — single-probe failover on a transient network blip is how you turn a non-incident into an incident. If you run Load Balancing, monitors (GET /accounts/$ACCOUNT_ID/load_balancers/monitors) do the same probing but with teeth: an unhealthy origin is automatically pulled from the pool. This is native automation at its best — but only for the one failure mode you configured in advance.

Tool 4: Audit logs — reviewing what changed

Every dashboard click and API write lands in the audit log. Pull the last two weeks and see who changed what:

curl "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/audit_logs?since=2026-06-29T00:00:00Z&per_page=100" \
  -H "Authorization: Bearer $CF_API_TOKEN" \
  | jq -r '.result[] | [.when, .actor.email, .action.type, .resource.type] | @tsv'

Schedule this weekly and actually read it. The findings are predictable and valuable: WAF rules disabled during an incident and never re-enabled, DNS edits from a token you thought was retired, settings changed by someone who left the on-call rotation months ago. Correlating "origin errors started Tuesday" with "someone changed the SSL mode Tuesday" is the single highest-value habit in Cloudflare operations — and the audit log is the raw material for it.

The ceiling: the API executes, it doesn't decide

Run everything above and you'll have a genuinely solid setup: scoped tokens, DNS and WAF drift detection, CI-driven cache purges, multi-region health probes, alerts in Slack. You'll also have hit the structural limit of native Cloudflare automation:

The API only executes decisions you already made. The failover script repoints DNS because you pre-decided what "down" means. The WAF rule blocks the path you pre-identified. Nothing native investigates a new situation.

Notifications deliver facts, not conclusions. A WAF-spike webhook at 2 a.m. still needs a human to query the security events, classify the traffic, decide whether to challenge or block, and write the rule. The alert compressed detection to seconds; the response is still measured in engineer-hours.

The scripts become the sprawl. Each cron job, each diff script, each webhook handler is code with an owner, a token to rotate, and edge cases discovered in production. Six months in, your automation layer needs its own audit.

Correlation is manual. The audit log knows what changed. The health check knows the origin is failing. Nothing native connects the two.

DIY automation raises the floor — it doesn't remove the human from the loop for anything that requires judgment.

What comes next

The missing layer is one that investigates: something that reads the WAF spike, correlates it with recent config changes and origin health, and proposes the fix — or applies it, if you've granted that autonomy. That's what CloudThinker agents do on top of Cloudflare, starting from a read-only scoped token, with every action gated by the autonomy level you configure. That's part three of this series. Or see it against your own zones now: Try CloudThinker free — 100 premium credits, no card required.