API Documentation
Everything you need to send notifications and handle responses.
Quick Start
Create a topic in your dashboard, then send your first notification:
# Send a plain-text push
curl -d "Hello from my Pi!" https://www.iotpush.com/api/push/my-topic
https://www.iotpush.com/...Three URL forms will appear to "almost work" but silently fail with"Redirecting..." — see Troubleshooting below for details.
http://...→ 308 redirect to https; curl drops POST body by defaultiotpush.com/...(apex, no www) → 307 redirect to www; same problemiotpush.com/your-topic(no/api/push/) → wrong path entirely
Troubleshooting — "Redirecting..."
If your curl command prints Redirecting... and no notification arrives, your URL is triggering an HTTP redirect that silently drops the POST body. This is the single most common integration issue. Here's how to diagnose and fix it.
Why it happens
By default, curl does not follow redirects, and even with -L it converts POST to GET on 301/302 unless you also pass --post301 --post302 --post307. The same gotcha exists in many Arduino / ESP32 HTTP libraries, older Python requests versions, and some webhook platforms. The cleanest fix is to never trigger a redirect in the first place.
Cause matrix
| URL you used | What the server does | Outcome |
|---|---|---|
| http://www.iotpush.com/api/push/my-topic | 308 → https://www.iotpush.com/... | curl prints "Redirecting..." and stops |
| http://iotpush.com/api/push/my-topic | 308 → https → 307 → www (two hops) | Body dropped on hop 2, even with -L |
| https://iotpush.com/api/push/my-topic | 307 → https://www.iotpush.com/... | Body dropped unless --post307 is set |
| iotpush.com/your-topic (no /api/push/) | Path doesn't exist | 404 / wrong route |
| https://www.iotpush.com/api/push/my-topic | Direct hit — no redirect | ✅ 200 OK, notification delivered |
Diagnose your own request
Run with -v to see the redirect chain, or --fail-with-body to surface real error responses:
# See exactly what the server returned (headers + body)
curl -v -d "test" https://www.iotpush.com/api/push/my-topic
# Treat 4xx/5xx as errors but still print the response body
curl --fail-with-body -sS \
-d "test" \
https://www.iotpush.com/api/push/my-topicIf you cannot change the URL
(e.g. legacy device, hard-coded webhook). Tell curl to follow redirects andpreserve the POST body across all redirect codes:
curl -L --post301 --post302 --post307 --post308 \
-d "Hello from my Pi!" \
http://iotpush.com/api/push/my-topicOther common errors
| Status | Body | Fix |
|---|---|---|
| 404 | Topic "X" not found | Create the topic in your dashboard first |
| 401 | Invalid API key | Check the Authorization: Bearer token for private topics |
| 405 | Method Not Allowed | Use POST, not GET — push endpoints only accept POST |
| 429 | Rate limit exceeded | You hit your monthly cap; upgrade or wait |
Send Notification
POST /api/push/{topic}Send a push notification to all subscribers of a topic. Accepts plain text or a JSON body. Private topics require an Authorization header with the topic API key.
Authentication (private topics)
# Bearer token = your topic API key
Authorization: Bearer YOUR_TOPIC_API_KEY
JSON Body Fields
| Field | Type | Required | Description |
|---|---|---|---|
| message | string | Yes | The notification body text |
| title | string | No | Notification title shown in bold |
| priority | string | No | low · normal (default) · high · urgent |
| tags | string | No | Comma-separated tags, e.g. "sensor,alert" |
| click_url | string | No | URL to open when the notification is tapped |
| actions | array | No | Interactive action buttons (see Action Buttons) |
| callback_url | string | No | Webhook URL called when an action is tapped |
Header Shortcuts
When sending plain text you can pass fields as HTTP headers instead of JSON.
| Header | Description | Example |
|---|---|---|
| Title | Notification title | Alert! |
| Priority | low, normal, high, urgent | high |
| Tags | Comma-separated tags | warning,sensor |
| Click | URL to open on tap | https://... |
Response
{
"success": true,
"id": "31d902cf-...", // message ID
"receipt_id": "b06e8c90-...", // use to poll delivery status
"topic": "my-topic",
"timestamp": "2026-04-10T00:06:01Z",
"subscribers": 8
}Examples
Simple:
curl -d "Sensor triggered!" https://www.iotpush.com/api/push/my-topic
With headers:
curl -H "Title: Alert" -H "Priority: high" \
-d "Temperature exceeded 80°C" \
https://www.iotpush.com/api/push/my-topicJSON body:
curl -H "Content-Type: application/json" \
-d '{"title":"Alert","message":"Temp high!","priority":"high"}' \
https://www.iotpush.com/api/push/my-topicPrivate topic (with API key):
curl -H "Authorization: Bearer YOUR_TOPIC_API_KEY" \
-H "Title: Alert" \
-d "Sensor triggered!" \
https://www.iotpush.com/api/push/my-topicTip — see real errors instead of silent failures:
curl --fail-with-body -sS \
-d "Sensor triggered!" \
https://www.iotpush.com/api/push/my-topicLanguage Examples
Python:
import requests
requests.post(
"https://www.iotpush.com/api/push/my-topic",
data="Hello from Python!",
headers={"Title": "Python Alert"}
)JavaScript / Node.js:
fetch("https://www.iotpush.com/api/push/my-topic", {
method: "POST",
body: "Hello from JS!",
headers: { "Title": "JS Alert" }
});Arduino / ESP32:
#include <HTTPClient.h>
HTTPClient http;
http.begin("https://www.iotpush.com/api/push/my-topic");
http.addHeader("Title", "ESP32 Alert");
http.POST("Sensor value: " + String(sensorValue));
http.end();Delivery Receipts
Every notification returns a receipt_id. Use receipts to track delivery, read, and action status.
Get a receipt
GET /api/receipts/{receipt_id}Auth: Authorization: Bearer USER_SESSION_TOKEN or X-Api-Key: TOPIC_API_KEY
{
"receipt_id": "b06e8c90-...",
"message_id": "31d902cf-...",
"topic_id": "...",
"status": "acted", // queued · delivered · read · acted · expired · failed
"delivered_count": 3,
"read_count": 2,
"acted_count": 1,
"actions_taken": [
{
"action_id": "approve",
"action_label": "Approve",
"reply_text": null,
"device_name": "Vincent's iPhone",
"acted_at": "2026-04-10T00:10:22Z"
}
],
"created_at": "2026-04-10T00:06:01Z",
"updated_at": "2026-04-10T00:10:22Z"
}List receipts
GET /api/receiptsQuery Parameters
| Param | Description |
|---|---|
| topic_id | Filter to a single topic |
| status | queued · delivered · read · acted · expired · failed |
| since | ISO 8601 timestamp — only return receipts after this time |
| cursor | Pagination cursor (created_at of last item) |
| limit | Max results per page (default 50, max 100) |
curl -H "Authorization: Bearer YOUR_SESSION_TOKEN" \
"https://www.iotpush.com/api/receipts?status=acted&limit=10"Report Action
Called automatically by the iotpush mobile app when a user taps an action button. You can also call this from your own client to mark an action as taken.
POST /api/actionAuth: Authorization: Bearer USER_SESSION_TOKEN
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| message_id | string | Yes | ID of the message the action belongs to |
| action_id | string | Yes | ID of the action that was tapped |
| reply_text | string | No | Text entered by user for reply-type actions |
| device_id | string | No | Device ID for attribution |
curl -X POST https://www.iotpush.com/api/action \
-H "Authorization: Bearer USER_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"message_id": "31d902cf-...",
"action_id": "approve",
"reply_text": null
}'Webhooks
Receive real-time HTTP callbacks when events occur on your topics — action taken, message delivered, read, or expired. Up to 10 webhooks per account.
Create a webhook
POST /api/webhooks| Field | Type | Required | Description |
|---|---|---|---|
| url | string | Yes | HTTPS endpoint to receive events |
| events | string[] | Yes | action · delivered · read · expired · all |
| topic_ids | string[] | No | Limit to specific topics (empty = all topics) |
curl -X POST https://www.iotpush.com/api/webhooks \
-H "Authorization: Bearer USER_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-server.com/hooks/iotpush",
"events": ["action", "delivered"]
}'Other webhook endpoints
| Method | Path | Description |
|---|---|---|
| GET | /api/webhooks | List all your webhooks |
| PATCH | /api/webhooks/{id} | Update url, events, active, or topic_ids |
| DELETE | /api/webhooks/{id} | Delete a webhook |
| POST | /api/webhooks/{id}/test | Send a test event to the webhook URL |
Webhook Payload (action event)
{
"event": "action",
"message_id": "31d902cf-...",
"topic": "my-topic",
"action_id": "approve",
"action_label": "Approve",
"reply_text": null,
"device_name": "Vincent's iPhone",
"acted_at": "2026-04-10T00:10:22Z"
}Devices
Register and manage push notification devices. The mobile app calls this automatically on login.
Register a device
POST /api/devices| Field | Type | Required | Description |
|---|---|---|---|
| push_token | string | Yes | Expo push token (ExponentPushToken[...]) |
| platform | string | Yes | ios · android · web |
| device_name | string | No | Human-readable name, e.g. "Vincent's iPhone" |
| app_version | string | No | App version string, e.g. "1.1.1" |
List devices
GET /api/devicesReturns all registered devices for the authenticated user, sorted by last seen.
Topics API
Programmatically manage your notification topics. All endpoints require a user session token (Authorization: Bearer USER_SESSION_TOKEN).
| Method | Path | Description |
|---|---|---|
| GET | /api/topics | List all your topics |
| POST | /api/topics | Create a new topic |
| PATCH | /api/topics/{id} | Update topic name, description, or privacy |
| DELETE | /api/topics/{id} | Delete a topic and all its messages |
Create topic body
{
"name": "server-alerts", // required, URL-safe slug
"description": "Production alerts",
"is_private": true // if true, API key required to push
}Pushover Compatibility
iotpush provides a Pushover-compatible endpoint. Apps with built-in Pushover support can use iotpush as a drop-in replacement.
POST https://www.iotpush.com/api/1/messages.jsonalso: POST https://www.iotpush.com/api/pushover
| Pushover Param | iotpush Mapping |
|---|---|
| token | Your iotpush topic API key |
| user | Topic name (optional if token is unique) |
| message | Notification message |
| title | Notification title |
| url | Click URL |
| priority | -2 to 2 → lowest/low/normal/high/urgent |
curl -s \ --form-string "token=YOUR_TOPIC_API_KEY" \ --form-string "user=my-topic" \ --form-string "message=Hello from Pushover-compatible API!" \ https://www.iotpush.com/api/1/messages.json
To migrate from Pushover: change the API URL from api.pushover.net to www.iotpush.com and use your topic API key as the token.
Rate Limits
| Plan | Messages/Month | Topics |
|---|---|---|
| Free | 1,000 | 3 |
| Pro ($9/mo) | 10,000 | 10 |
| Business ($29/mo) | 100,000 | Unlimited |
Integrations
iotpush works with any automation platform that can make HTTP requests — n8n, Make, Zapier, Home Assistant, and more.
See our full integration guides for step-by-step setup.