
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.
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:
| Field | Type | Required | Description |
|---|---|---|---|
event | string | ✅ | Your event key. Use dot-notation: payments.failed |
title | string | ✅ | Push notification title (shown in system tray) |
body | string | ✅ | Push notification body text |
priority | string | — | LOW, NORMAL, HIGH, URGENT. Default: NORMAL |
data | object | — | Arbitrary JSON metadata attached to the event |
url | string | — | Deep link URL that opens when user taps the notification |
dedupeKey | string | — | If 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)
16endGo
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
| Priority | Use Case | Behavior |
|---|---|---|
LOW | Informational background events | Silent notification / badge only |
NORMAL | Standard business events | Shows notification banner |
HIGH | Urgent business events | Sound + banner |
URGENT | Incidents, failures, pager-level | Loud 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
| Plan | Events/month | Notifications/month |
|---|---|---|
| Free | 1,000 | 100 |
| Pro | 50,000 | 10,000 |
| Business | Unlimited | Unlimited |
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.


