API Reference
Send a push notification to any Beeping device with a single HTTP request. You'll need two things: an API key from your own account (identifies and bills you, the sender) and a pairing code from the device you want to page (identifies the receiver).
Overview
Every Beeping device can generate one or more pairing codes from Settings and share them with whoever should be able to page it. Separately, every sender generates their own API key, also from Settings. Sends made with your key are billed against your own account — your free monthly quota or your subscription — never against the receiving device's. This is what lets you hand out your pairing code freely without worrying that someone else's scripted sends will eat into your own allowance.
404.
Authentication
All routes are served from a single base path: https://beeping.macspot.net/api
Sending a page requires an X-API-Key header. This key is tied to your
account and is what determines who gets billed for the send — not the pairing code, which
only identifies the receiver. Treat your API key like a password: it's shown once at
creation time and can't be retrieved again, only revoked and replaced.
401.
Quickstart
Get an API key and a pairing code, then send your first page in under a minute.
1. Create an API key
Open Beeping → Settings → API Keys → Create API Key. Copy the key immediately — it's only ever shown once.
2. Get a pairing code for the device you want to page
On the receiving device: Settings → Pairing Codes → Add Pairing Code. Tap the copy icon next to a code to copy a ready-to-run curl example (minus your key).
3. Send a page
curl -X POST https://beeping.macspot.net/api/sendByPairingCode \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"pairingCode": "AB3D9F",
"title": "Deploy failed",
"body": "staging build #4821 failed at the lint step",
"severity": "high"
}'
That's it — the push arrives on the device that generated the pairing code, immediately. The send counts against your own account's quota, not theirs.
API keys
API keys are 8 characters long, generated and managed from Settings → API Keys in the app. There's currently no HTTP endpoint for key management — creating, listing, and revoking keys is done from the app itself, since it's tied to your signed-in account.
| Action | Where |
|---|---|
| Create a key | Settings → API Keys → Create API Key. The plaintext key is shown once — save it immediately. |
| List your keys | Settings → API Keys shows all your keys by a short, non-reversible identifier plus creation/last-used time. The plaintext value is never shown again after creation. |
| Revoke a key | Tap the trash icon next to a key in Settings → API Keys. Revocation is immediate — any request using that key afterward gets 401. |
Send a page
POST https://beeping.macspot.net/api/sendByPairingCode
Sends a push notification to whichever device is currently paired to pairingCode. Billed against the account that owns the X-API-Key used, not the receiving device's account.
Headers
| Header | Description |
|---|---|
| X-API-Keyrequired | Your API key, from Settings → API Keys. Identifies you as the sender for billing/quota purposes. |
Request body
| Field | Type | Description |
|---|---|---|
| pairingCoderequired | string | The code generated from the receiving device's Settings tab. Case-insensitive. |
| titlerequired | string | Notification title shown on the receiving device. |
| bodyrequired | string | Notification body text. |
| severityoptional | string | One of normal, high, or critical. Defaults to normal. See Severity levels. |
| webhookUrloptional | string | A URL under your control that accepts a POST request. If set, it's delivered to the receiving device along with the page. If the recipient acknowledges the page from their app, their device POSTs an acknowledgment payload to this URL — see Acknowledge webhook payload. This is your predefined contract, not something the receiving device configures. There's no signature or shared secret on the request the device sends — if you need to verify it's authentic, embed your own token directly in the URL (e.g. as a query parameter) when you set it here. |
Example request
curl -X POST https://beeping.macspot.net/api/sendByPairingCode \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"pairingCode": "AB3D9F",
"title": "Production DB unreachable",
"body": "prod-db-1 failed 3 health checks in a row",
"severity": "critical",
"webhookUrl": "https://your-system.example.com/paging-ack"
}'
Example response
{
"ok": true,
"alertId": "3f1b6c2a-...",
"sent": 1,
"failed": 0,
"quotaRemaining": 287
}
alertId uniquely identifies the page that was just sent. It matches the
alertId in the acknowledgment webhook payload, so you can correlate a send
with its later acknowledgment.
quotaRemaining shows how many sends your account (the API key's
owner) has left this calendar month under your plan's monthly send allowance.
When nothing gets sent
Some conditions return 200 with sent: 0 rather than an error,
since they're expected outcomes, not failures — for example, the receiving device has
Do Not Disturb enabled (and you sent normal or high severity —
see Severity levels, critical always gets through), or has
disabled critical alerts specifically and you sent a critical severity page.
{
"ok": true,
"sent": 0,
"message": "Target device has Do Not Disturb enabled"
}
Severity levels
Severity controls how the notification interrupts the receiving device.
| Value | Behavior |
|---|---|
| normal | Standard notification. Respects Focus modes, the silent switch, and Do Not Disturb normally. |
| high | Time-sensitive interruption level — can break through some Focus modes. Still respects Do Not Disturb. |
| critical | Highest priority. Bypasses Do Not Disturb — Do Not Disturb is meant to pause routine notifications, not actual incidents. Skipped entirely only if the receiving device has disabled critical alerts specifically in Settings (independent of Do Not Disturb). |
Acknowledge webhook payload
If you set webhookUrl on a page, it travels with that specific page to the
receiving device, along with the pairingCode it was sent to. If the
recipient taps Acknowledge on the page's detail screen, their device
POSTs the following JSON body to that URL:
{
"event": "page.acknowledged",
"alertId": "3f1b6c2a-...",
"pairingCode": "AB3D9F",
"severity": "critical",
"receivedAt": "2026-07-18T09:14:02Z",
"acknowledgedAt": "2026-07-18T09:16:41Z"
}
alertId uniquely identifies this specific message. pairingCode
identifies which of your recipients acknowledged it — useful if you send through several
different pairing codes and need to tell them apart.
This is a best-effort notification: the acknowledgment is always recorded locally on the
receiving device regardless of whether your webhook endpoint is reachable or returns an
error. Your endpoint should respond with a 2xx status; the device doesn't
retry on failure. webhookUrl is entirely optional — omit it and
acknowledgment stays local to the receiving device.
webhookUrl — for example
https://your-system.example.com/paging-ack?token=YOUR_SECRET. Check for that
token on your end when the request arrives.
Rate limits & quota
Per-code rate limit
Requests against the same pairing code are limited to roughly one every 3 seconds. A
request that arrives sooner receives a 429. This exists to blunt naive
retry loops and brute-force attempts against a single code, not to constrain normal
usage.
Monthly send quota
Each plan has a monthly send allowance, tracked per sender (the account that owns the API key used) and reset automatically at the start of each month: Free gets 300 sends, Pro gets 3,000, and Premium gets 30,000. Quota is consumed only on requests that actually result in a send attempt — an unknown pairing code or a device that's off-call doesn't count against it.
Errors
| Status | Meaning |
|---|---|
| 400 | pairingCode, title, or body missing from the request. |
| 401 | Missing or invalid X-API-Key header. Check that the key hasn't been revoked from Settings → API Keys. |
| 402 | Your (the sender's) free monthly send quota is exhausted. Response body includes {"error": "quota_exceeded"}. |
| 404 | The pairing code doesn't exist (never issued, revoked, or replaced), the receiving device is no longer registered, or it has no push token yet. |
| 429 | Too many requests against this pairing code in a short window. Response body includes {"error": "rate_limited"}. Wait a few seconds and retry. |
| 500 | Unexpected server error. Safe to retry with backoff. |
curl
curl -X POST https://beeping.macspot.net/api/sendByPairingCode \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"pairingCode": "AB3D9F",
"title": "Backup finished",
"body": "412 GB archived to cold storage",
"severity": "normal"
}'
Node.js
async function sendPage({ title, body, severity = "normal" }) {
const response = await fetch(
"https://beeping.macspot.net/api/sendByPairingCode",
{
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": process.env.PAGING_API_KEY,
},
body: JSON.stringify({
pairingCode: process.env.PAGING_CODE,
title,
body,
severity,
}),
}
);
if (!response.ok) {
throw new Error(`Page failed: ${response.status}`);
}
return response.json();
}
await sendPage({
title: "Deploy failed",
body: "staging build #4821 failed at the lint step",
severity: "high",
});
Python
import os
import requests
def send_page(title: str, body: str, severity: str = "normal") -> dict:
response = requests.post(
"https://beeping.macspot.net/api/sendByPairingCode",
headers={"X-API-Key": os.environ["PAGING_API_KEY"]},
json={
"pairingCode": os.environ["PAGING_CODE"],
"title": title,
"body": body,
"severity": severity,
},
timeout=10,
)
response.raise_for_status()
return response.json()
send_page(
title="Production DB unreachable",
body="prod-db-1 failed 3 health checks in a row",
severity="critical",
)
Shell script monitoring example
A common pattern: wrap a health check in a cron job, and page yourself only when it fails.
#!/usr/bin/env bash
set -euo pipefail
PAGING_API_KEY="YOUR_API_KEY"
PAGING_CODE="AB3D9F"
ENDPOINT="https://beeping.macspot.net/api/sendByPairingCode"
if ! curl -sf https://example.com/health > /dev/null; then
curl -X POST "$ENDPOINT" \
-H "X-API-Key: $PAGING_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"pairingCode\":\"$PAGING_CODE\",\"title\":\"Health check failed\",\"body\":\"example.com/health did not return 200\",\"severity\":\"critical\"}"
fi
Schedule it with cron, launchd, or your CI system's scheduled-job feature. Store PAGING_API_KEY as a secret, not a plaintext script variable, in real deployments.