FlowAlert
All posts
Send Push Notifications from Any REST API: The Complete Developer Guide
apipush-notificationsdevelopertutorial

Send Push Notifications from Any REST API: The Complete Developer Guide

A language-agnostic guide to sending push notifications from any backend using the FlowAlert REST API — with examples in Node.js, Python, PHP, Ruby, Go, and cURL.

FlowAlert TeamMay 12, 2026 5 min read

Send Push Notifications from Any REST API: The Complete Developer Guide

Push notifications shouldn't require a Firebase project, a service worker, VAPID keys, or a third-party SDK. If your backend can make an HTTP request, it can send a push notification.

This guide covers the FlowAlert Events API — a single REST endpoint you can call from any language, any framework, any infrastructure.


The API in 30 Seconds

1curl -X POST https://api.flowalert.io/v1/events \ 2 -H "Content-Type: application/json" \ 3 -H "X-Api-Key: fa_live_xxxxxxxxxxxx" \ 4 -d '{ 5 "event": "server.cpu_spike", 6 "title": "⚠️ High CPU Usage", 7 "body": "web-01 is at 94% CPU for 5 minutes", 8 "priority": "URGENT" 9 }'

That's it. If you have a FlowAlert account and the mobile app installed, that curl command will push a notification to your phone.


API Reference

Endpoint: POST https://api.flowalert.io/v1/events

Authentication: X-Api-Key: <your_api_key> header

Request body:

FieldTypeRequiredDescription
eventstringYour event key. Use dot-notation: payments.failed
titlestringPush notification title (shown in system tray)
bodystringPush notification body text
prioritystringLOW, NORMAL, HIGH, URGENT. Default: NORMAL
dataobjectArbitrary JSON metadata attached to the event
urlstringDeep link URL that opens when user taps the notification
dedupeKeystringIf provided, duplicate events with the same key within 10 minutes are ignored

Response:

{ "ok": true, "eventId": "cm_xxxxxxxxxxxxxxxx", "eventType": "server.cpu_spike" }

Examples by Language

Node.js (native fetch)

1async function notify({ event, title, body, priority = 'NORMAL', data = {} }) { 2 const res = await fetch('https://api.flowalert.io/v1/events', { 3 method: 'POST', 4 headers: { 5 'Content-Type': 'application/json', 6 'X-Api-Key': process.env.FLOWALERT_API_KEY, 7 }, 8 body: JSON.stringify({ event, title, body, priority, data }), 9 }); 10 return res.json(); 11} 12 13// Usage 14await notify({ 15 event: 'user.signup', 16 title: '🎉 New User', 17 body: 'alice@example.com just signed up', 18 priority: 'NORMAL', 19 data: { email: 'alice@example.com' }, 20});

Python

1import os 2import requests 3 4def notify(event: str, title: str, body: str, priority: str = "NORMAL", data: dict = None): 5 requests.post( 6 "https://api.flowalert.io/v1/events", 7 headers={ 8 "Content-Type": "application/json", 9 "X-Api-Key": os.environ["FLOWALERT_API_KEY"], 10 }, 11 json={ 12 "event": event, 13 "title": title, 14 "body": body, 15 "priority": priority, 16 "data": data or {}, 17 }, 18 timeout=5, 19 ) 20 21# Usage 22notify( 23 event="model.training_complete", 24 title="✅ Model Training Done", 25 body="GPT fine-tune job completed in 4h 23m", 26 priority="HIGH", 27)

PHP

1function flowalert_notify(string $event, string $title, string $body, string $priority = 'NORMAL', array $data = []): void { 2 $ch = curl_init('https://api.flowalert.io/v1/events'); 3 curl_setopt_array($ch, [ 4 CURLOPT_RETURNTRANSFER => true, 5 CURLOPT_POST => true, 6 CURLOPT_HTTPHEADER => [ 7 'Content-Type: application/json', 8 'X-Api-Key: ' . ($_ENV['FLOWALERT_API_KEY'] ?? ''), 9 ], 10 CURLOPT_POSTFIELDS => json_encode(compact('event', 'title', 'body', 'priority', 'data')), 11 CURLOPT_TIMEOUT => 5, 12 ]); 13 curl_exec($ch); 14 curl_close($ch); 15}

Ruby

1require 'net/http' 2require 'json' 3require 'uri' 4 5def notify(event:, title:, body:, priority: 'NORMAL', data: {}) 6 uri = URI.parse('https://api.flowalert.io/v1/events') 7 http = Net::HTTP.new(uri.host, uri.port) 8 http.use_ssl = true 9 10 req = Net::HTTP::Post.new(uri.path) 11 req['Content-Type'] = 'application/json' 12 req['X-Api-Key'] = ENV['FLOWALERT_API_KEY'] 13 req.body = { event:, title:, body:, priority:, data: }.to_json 14 15 http.request(req) 16end

Go

1package notify 2 3import ( 4 "bytes" 5 "encoding/json" 6 "net/http" 7 "os" 8) 9 10type Event struct { 11 Event string `json:"event"` 12 Title string `json:"title"` 13 Body string `json:"body"` 14 Priority string `json:"priority"` 15 Data map[string]interface{} `json:"data,omitempty"` 16} 17 18func Send(e Event) error { 19 if e.Priority == "" { 20 e.Priority = "NORMAL" 21 } 22 23 payload, _ := json.Marshal(e) 24 req, _ := http.NewRequest("POST", "https://api.flowalert.io/v1/events", bytes.NewBuffer(payload)) 25 req.Header.Set("Content-Type", "application/json") 26 req.Header.Set("X-Api-Key", os.Getenv("FLOWALERT_API_KEY")) 27 28 client := &http.Client{} 29 resp, err := client.Do(req) 30 if err != nil { 31 return err 32 } 33 defer resp.Body.Close() 34 return nil 35}

Deduplication

If you have a monitoring loop that fires every 30 seconds, you don't want 20 notifications for the same alert. Use dedupeKey:

1curl -X POST https://api.flowalert.io/v1/events \ 2 -H "X-Api-Key: $FLOWALERT_KEY" \ 3 -H "Content-Type: application/json" \ 4 -d '{ 5 "event": "db.connection_pool_exhausted", 6 "title": "🔴 DB Pool Exhausted", 7 "body": "postgres-primary is rejecting connections", 8 "priority": "URGENT", 9 "dedupeKey": "db-pool-alert" 10 }'

If you fire this every 30 seconds, you'll only get one notification per 10-minute window. The next one only fires after the dedup window resets.


Priority Levels Explained

PriorityUse CaseBehavior
LOWInformational background eventsSilent notification / badge only
NORMALStandard business eventsShows notification banner
HIGHUrgent business eventsSound + banner
URGENTIncidents, failures, pager-levelLoud alert sound, high-priority delivery

Best Practices for Event Keys

Use a consistent dot-notation taxonomy so routing rules stay manageable:

{service}.{resource}.{action} Examples: payments.invoice.paid auth.user.registered infra.server.cpu_high infra.deploy.failed ecommerce.order.placed ecommerce.refund.requested

This makes your routing rules easier to manage — you can group related events under the same resource prefix (e.g. infra) and create one rule per resource type.


Rate Limits

PlanEvents/monthNotifications/month
Free1,000100
Pro50,00010,000
BusinessUnlimitedUnlimited

For high-frequency monitoring use cases (e.g. healthcheck loops), use dedupeKey to stay within limits and avoid alert fatigue.


Summary

If it can make an HTTP POST, it can send a push notification. One endpoint, one API key, notifications to any mobile device — no service worker setup, no SDK, no Firebase console.

Create your free API key →

Get alerts where you’ll actually see them.

Everything in these guides works on the free plan — connect a source, add a routing rule, and the right person’s phone knows in under two minutes.

No card required · Free plan forever · 1,000 events/month