Cloudflare notification notifications on your phone.

Get a push notification for any Cloudflare notification, loudest for DDoS attacks and origin errors and quiet for informational updates and resolved alerts.

How it works

Three steps to your first push

STEP 01

Install the recipe

One click in Studio gives you a personal webhook URL.

STEP 02

Paste your URL into Cloudflare

Add it as a webhook for Notification.

STEP 03

Get a push on your phone

With the text, sound and buttons from the recipe.

Setup

How to set up the Cloudflare notification webhook

  1. Click Install in Studio and sign in. The recipe is added to your account and you get a personal webhook URL.
  2. In the Cloudflare dashboard, go to Notifications > Destinations and select Create in the Webhooks card (needs at least one Pro zone).
  3. Name it, paste your webhook URL as the URL and select Save and Test.https://••••••••/•••••••• your personal URL, shown after install
  4. Under Notifications, add or edit the notifications you want and pick this webhook as a destination.
  5. In the Cloudflare dashboard, go to **Notifications → Destinations** and select **Create** in the **Webhooks** card. Webhook destinations need at least one zone on a Pro plan or higher.
  6. Name it, paste the URL above and select **Save and Test**. The test gives a quiet **🔔 Cloudflare connected** push.
  7. Under **Notifications**, add the notifications you care about (DDoS attacks, origin error rate, health checks, SSL certificates, tunnel health, Workers usage …) and choose this webhook as their destination.
  8. The push shows Cloudflare's own text. The alert type sets how loud it is: **Highest** for DDoS attacks, origin errors and BGP hijacks, **High** for health checks, tunnels, error rates and security findings, **Normal** for certificates, usage and anything the recipe doesn't know, **Low** for maintenance, digests and resolved alerts.
  9. Cloudflare can send a secret in the `cf-webhook-auth` header, but JustPush doesn't check it yet, so keep your endpoint URL private.
The code

What runs when the webhook arrives

Studio calls handleRequest(request) with the incoming webhook and sends the message it returns. It's yours after install; change anything.

transform.js
1// Cloudflare → any notification sent to a generic webhook destination
2// Fires for every Cloudflare notification policy that uses this webhook. The push uses
3// Cloudflare's own text; the alert type sets the label and how loud it is.
4// Docs: https://developers.cloudflare.com/notifications/reference/webhook-payload-schema/
5
6// alert_type → label and priority.
7// 2: under attack or the site is down right now.
8// 1: needs you soon (errors, failing health checks, security findings).
9// 0: worth knowing (certificates, usage, deploys).
10// -1: informational digests and maintenance.
11const TYPES = {
12 advanced_ddos_attack_l4_alert: { emoji: "🛡️", label: "DDoS attack", priority: 2 },
13 advanced_ddos_attack_l7_alert: { emoji: "🛡️", label: "HTTP DDoS attack", priority: 2 },
14 dos_attack_l4: { emoji: "🛡️", label: "DDoS attack", priority: 2 },
15 dos_attack_l7: { emoji: "🛡️", label: "HTTP DDoS attack", priority: 2 },
16 fbm_dosd_attack: { emoji: "🛡️", label: "DDoS attack on your network", priority: 2 },
17 fbm_volumetric_attack: { emoji: "🛡️", label: "Volumetric attack on your network", priority: 2 },
18 bgp_hijack_notification: { emoji: "🚨", label: "BGP hijack detected", priority: 2 },
19 http_alert_origin_error: { emoji: "🔥", label: "Origin error rate high", priority: 2 },
20 real_origin_monitoring: { emoji: "🔥", label: "Origin unreachable", priority: 2 },
21 load_balancing_health_alert: { emoji: "⚠️", label: "Load balancer pool health changed", priority: 1 },
22 health_check_status_notification: { emoji: "⚠️", label: "Health check status changed", priority: 1 },
23 tunnel_health_event: { emoji: "⚠️", label: "Tunnel health changed", priority: 1 },
24 magic_tunnel_health_check_event: { emoji: "⚠️", label: "Magic tunnel health changed", priority: 1 },
25 magic_wan_tunnel_health: { emoji: "⚠️", label: "Magic WAN tunnel health changed", priority: 1 },
26 http_alert_edge_error: { emoji: "⚠️", label: "Edge error rate high", priority: 1 },
27 advanced_http_alert_error: { emoji: "⚠️", label: "HTTP error rate high", priority: 1 },
28 traffic_anomalies_alert: { emoji: "⚠️", label: "Traffic anomaly", priority: 1 },
29 clickhouse_alert_fw_anomaly: { emoji: "⚠️", label: "Security events spike", priority: 1 },
30 clickhouse_alert_fw_ent_anomaly: { emoji: "⚠️", label: "Security events spike", priority: 1 },
31 bot_traffic_basic_alert: { emoji: "🤖", label: "Bot traffic spike", priority: 1 },
32 custom_bot_detection_alert: { emoji: "🤖", label: "Bot detection alert", priority: 1 },
33 scriptmonitor_alert_new_malicious_scripts: { emoji: "🚨", label: "Malicious script detected", priority: 1 },
34 scriptmonitor_alert_new_malicious_hosts: { emoji: "🚨", label: "Malicious script host detected", priority: 1 },
35 scriptmonitor_alert_new_malicious_url: { emoji: "🚨", label: "Malicious URL detected", priority: 1 },
36 abuse_report_alert: { emoji: "⚠️", label: "Abuse report", priority: 1 },
37 block_notification_new_block: { emoji: "⛔", label: "Content blocked", priority: 1 },
38 secondary_dns_all_primaries_failing: { emoji: "🔥", label: "All DNS primaries failing", priority: 1 },
39 secondary_dns_primaries_failing: { emoji: "⚠️", label: "DNS primaries failing", priority: 1 },
40 failing_logpush_job_disabled_alert: { emoji: "⚠️", label: "Logpush job disabled", priority: 1 },
41 workers_observability_alert: { emoji: "⚠️", label: "Workers alert", priority: 1 },
42 synthetic_test_low_availability_alert: { emoji: "⚠️", label: "Low availability", priority: 1 },
43 device_connectivity_anomaly_alert: { emoji: "⚠️", label: "Device connectivity anomaly", priority: 1 },
44 sentinel_alert: { emoji: "⚠️", label: "Sentinel alert", priority: 1 },
45 universal_ssl_event_type: { emoji: "🔒", label: "Universal SSL certificate", priority: 0 },
46 dedicated_ssl_certificate_event_type: { emoji: "🔒", label: "SSL certificate", priority: 0 },
47 custom_ssl_certificate_event_type: { emoji: "🔒", label: "Custom SSL certificate", priority: 0 },
48 access_custom_certificate_expiration_type: { emoji: "🔒", label: "Access certificate expiring", priority: 0 },
49 hostname_aop_custom_certificate_expiration_type: { emoji: "🔒", label: "Origin pull certificate expiring", priority: 0 },
50 zone_aop_custom_certificate_expiration_type: { emoji: "🔒", label: "Origin pull certificate expiring", priority: 0 },
51 mtls_certificate_store_certificate_expiration_type: { emoji: "🔒", label: "mTLS certificate expiring", priority: 0 },
52 expiring_service_token_alert: { emoji: "🔑", label: "Service token expiring", priority: 0 },
53 workers_alert: { emoji: "📈", label: "Workers usage", priority: 0 },
54 billing_usage_alert: { emoji: "💳", label: "Usage alert", priority: 0 },
55 pages_event_alert: { emoji: "📄", label: "Pages deployment", priority: 0 },
56 incident_alert: { emoji: "📢", label: "Cloudflare incident", priority: 0 },
57 synthetic_test_latency_alert: { emoji: "🐢", label: "High latency", priority: 0 },
58 secondary_dns_warning: { emoji: "⚠️", label: "Secondary DNS warning", priority: 0 },
59 secondary_dns_zone_validation_warning: { emoji: "⚠️", label: "Secondary DNS warning", priority: 0 },
60 load_balancing_pool_enablement_alert: { emoji: "⚖️", label: "Load balancer pool toggled", priority: 0 },
61 scriptmonitor_alert_new_hosts: { emoji: "📜", label: "New script host", priority: 0 },
62 scriptmonitor_alert_new_resources: { emoji: "📜", label: "New script", priority: 0 },
63 scriptmonitor_alert_new_code_change_detections: { emoji: "📜", label: "Script changed", priority: 0 },
64 scriptmonitor_alert_new_max_length_resource_url: { emoji: "📜", label: "Long script URL", priority: 0 },
65 fbm_auto_advertisement: { emoji: "🛡️", label: "Prefix auto-advertised", priority: 1 },
66 maintenance_event_notification: { emoji: "🔧", label: "Scheduled maintenance", priority: -1 },
67 cni_maintenance_notification: { emoji: "🔧", label: "Interconnect maintenance", priority: -1 },
68 secondary_dns_zone_successfully_updated: { emoji: "ℹ️", label: "Secondary DNS zone updated", priority: -1 },
69 block_notification_block_removed: { emoji: "ℹ️", label: "Block removed", priority: -1 },
70 web_analytics_metrics_update: { emoji: "📊", label: "Web Analytics update", priority: -1 },
71 radar_notification: { emoji: "📡", label: "Radar notification", priority: -1 },
72 brand_protection_digest: { emoji: "ℹ️", label: "Brand protection digest", priority: -1 },
73}
74
75// Look a key up in one of the tables above, ignoring inherited names like "toString".
76function pick(table, key, fallback) {
77 return Object.prototype.hasOwnProperty.call(table, key) ? table[key] : fallback
78}
79
80// Only link to real http(s) URLs.
81function link(url, cta) {
82 return typeof url === "string" && /^https?:\/\//.test(url) ? [{ cta, url }] : []
83}
84
85// A plain, non-empty string or null.
86function text(value) {
87 return typeof value === "string" && value.trim() ? value.trim() : null
88}
89
90function handleRequest(request) {
91 const body = request.body && typeof request.body === "object" ? request.body : {}
92 const data = body.data && typeof body.data === "object" ? body.data : {}
93 const message = text(body.text)
94 const alertType = text(body.alert_type)
95 const policy = text(body.name) ?? text(body.policy_name)
96
97 // "Save and Test" sends only a text field; confirm the connection quietly.
98 if (message && !alertType && !policy && /test message/i.test(message)) {
99 return {
100 title: "🔔 Cloudflare connected",
101 message: "Your Cloudflare webhook destination is working",
102 topic: "Cloudflare",
103 priority: -1,
104 }
105 }
106
107 // Guard: a Cloudflare notification has text plus an alert type or policy name.
108 if (!message || (!alertType && !policy)) {
109 console.log("Not a Cloudflare notification — skipping")
110 return null
111 }
112
113 const type = pick(TYPES, alertType, null)
114 // An alert that ended, or a health check that turned healthy, is good news.
115 const healthy = /^healthy$/i.test(text(data.new_health_status) ?? text(data.new_status) ?? "")
116 const resolved = body.alert_event === "ALERT_STATE_EVENT_END" || healthy
117 const label = type?.label ?? policy ?? "Cloudflare notification"
118 // Name the zone or host when Cloudflare's text doesn't already.
119 const where = text(data.target_hostname) ?? text(data.zone_name)
120 const title = resolved
121 ? `✅ Resolved: ${label}`
122 : `${type?.emoji ?? "🔔"} ${label}${where && !message.includes(where) ? ` on ${where}` : ""}`
123
124 // Dashboard links: the alert's own link when it has one, otherwise the account.
125 const account = text(body.account_id)
126 const dashboard = text(data.dashboard_link) ?? text(data.rule_link) ??
127 (account && /^[0-9a-f]{32}$/i.test(account) ? `https://dash.cloudflare.com/${account}` : null)
128
129 return {
130 title,
131 message: message.length > 500 ? `${message.slice(0, 497)}…` : message,
132 topic: "Cloudflare",
133 // Resolved alerts are good news; unknown alert types default to Normal.
134 priority: resolved ? -1 : type?.priority ?? 0,
135 buttons: link(dashboard, "Open dashboard"),
136 }
137}
Payload

The Cloudflare notification webhook

This is what Cloudflare sends to your URL for notification. It is a sample, trimmed to the fields recipes use.

notification · sample.json
1{
2 "ts": 1790671200,
3 "data": {
4 "action": "block",
5 "rule_id": "fdfdac75430c4c47a959592f0aa5e68a",
6 "max_rate": "184000",
7 "zone_tag": "023e105f4ecef8ad9ca31a8372d0c353",
8 "attack_id": "a1b2c3d4e5f6",
9 "zone_name": "example.com",
10 "mitigation": "block",
11 "ruleset_id": "4d21379b4f9f4bb088e0729962c8b3cf",
12 "start_time": "2026-09-29T08:40:00Z",
13 "account_tag": "9035f53656c247e895c5a6939ae8a0e0",
14 "attack_type": "HTTP flood",
15 "account_name": "Acme",
16 "dashboard_link": "https://dash.cloudflare.com/9035f53656c247e895c5a6939ae8a0e0/example.com/security/events",
17 "target_hostname": "shop.example.com",
18 "rule_description": "HTTP requests from known botnet signatures",
19 "requests_per_second": 184000
20 },
21 "name": "DDoS alerts",
22 "text": "Cloudflare is mitigating an HTTP DDoS attack against shop.example.com, peaking at 184,000 requests per second.",
23 "policy_id": "749b911ea5d04344a58e45edd099b328",
24 "account_id": "9035f53656c247e895c5a6939ae8a0e0",
25 "alert_type": "advanced_ddos_attack_l7_alert",
26 "alert_event": "ALERT_STATE_EVENT_START",
27 "policy_name": "DDoS alerts",
28 "alert_correlation_id": "000eaa907ed24e78946d3a93adb2ae57"
29}
FAQ

Cloudflare notification notifications: questions

Does this work on iPhone and Android?

Yes. Install the JustPush app from the App Store or Google Play and sign in. Every phone signed in to your account gets the notification.

Do I need to write code?

No. Install the recipe in Studio, paste your webhook URL into Cloudflare and you are done. The code is there if you want to change the text, the sound or the buttons.

Can I change what the notification says?

Yes. After install the recipe's code is yours. Edit it in Studio and test it against the sample payload before you save.

What does it cost?

JustPush is free for 30 days. After that it's $19.99 a year, or $39.99 once. There is no extra charge for recipes.

Ready when you are

Cloudflare on your phone in two minutes.

Install the recipe, paste one URL, done. Free for 30 days, no credit card required.