Skip to main content

API Reference

Authentication

All API requests require authentication. Two methods are available: a personal API key (for most integrations) and a JWT token (for web applications and endpoints that don’t yet support the key).

API Key

Create a personal API key from your profile menu. Use the X-API-Key header:

curl -H "X-API-Key: YOUR_API_KEY" \
  https://pingzen.dev/api/v1/monitors

To create an API key: Click on your profile → API Keys → Create New Key

Where X-API-Key works, and where JWT is required

Important

At this time, X-API-Key is accepted only on the monitors endpoints (CRUD, pause/resume, reorder, main statistics, timeline, and CSV export/import). All other endpoints — /workspaces, /alerts, /incidents, /status-pages, /monitor-groups, /probes, /heartbeats (except the no-auth /ping/...), /maintenance, /feedback, /dashboard, /web-push, /scheduled-reports, /webhooks, /webhook-checks, /api-checks, /api-keys, /auth/*, /analytics, /telegram-bot, /alert-channel-configs — currently require a JWT token. This is a divergence from the historical promise of the docs; we’re closing it incrementally.

What X-API-Key covers right now:

GET/api/v1/monitorsCRUD (GET/POST/PATCH/DELETE), /reorder, /pause, /resume, /reset-stats, /results, /timeline, /status-ranges, /response-time-distribution, /cert-chain, /dnsbl-providers, /export/csv, /import/csv
POST/api/v1/ping/:ping_key/:slugno auth (uses ping_key) — for heartbeats

Inside /monitors/*, these still require JWT (known divergence, being fixed): GET /:id/stats, POST /:id/check, GET /:id/sla, GET /:id/sla/export/csv|pdf, GET /:id/dnsbl/provider-history, POST /test.

JWT Token

For web applications and endpoints that don’t accept the API key, use a JWT from /auth/login:

# 1. Obtain a token (pass email and password)
ACCESS=$(curl -sS -X POST https://pingzen.dev/api/v1/auth/login \
  -H 'Content-Type: application/json' \
  -d '{"email":"you@example.com","password":"…"}' \
  | python3 -c 'import json,sys; print(json.load(sys.stdin)["access_token"])')

# 2. Use it as Authorization: Bearer
curl -H "Authorization: Bearer $ACCESS" https://pingzen.dev/api/v1/workspaces

The access token lives for 15 minutes; for long-running integrations use the refresh_token from the same response and the /auth/refresh endpoint.

Endpoints

Complete list of all available API endpoints.

Monitors

Monitor management (22 endpoints, grouped by purpose).

CRUD

GET/api/v1/monitors— List your monitors
POST/api/v1/monitors— Create a monitor
GET/api/v1/monitors/:id— Get monitor details
PATCH/api/v1/monitors/:id— Update monitor
DELETE/api/v1/monitors/:id— Delete monitor

Operations

POST/api/v1/monitors/:id/pause— Pause monitoring
POST/api/v1/monitors/:id/resume— Resume monitoring
POST/api/v1/monitors/:id/check— Run manual check (JWT)
POST/api/v1/monitors/:id/reset-stats— Wipe accumulated history (requires confirm)
PUT/api/v1/monitors/reorder— Set custom display order
POST/api/v1/monitors/test— Test a config without saving (JWT)
GET/api/v1/monitors/export/csv— Export monitor configs as CSV (all workspaces or ?workspace_id=; secrets excluded)
POST/api/v1/monitors/import/csv— Bulk-create monitors from a CSV file (multipart; ?dry_run=true to preview, ?on_duplicate=skip|create; max 500 rows / 1 MB)

CSV import notes: you only need a url column. Files from StatusCake, the UptimeRobot bulk-upload template, and HetrixTools work as they are (, and ; are both detected). A row is a duplicate when the same URL and protocol already exist in the target workspace; duplicates are skipped by default. Without ?workspace_id=, each row goes to the workspace named in its workspace column, and a row with no workspace goes to your default workspace. Monitor names must be unique in your account, so copying one of your own workspaces into another one needs new names — name clashes go to skipped with the reason. You can import into your own workspaces, and into shared workspaces where you are an editor or owner (viewers get 403 or a per-row error). UptimeRobot number types (1/2/3/4) and StatusCake DoNotFind are supported. Excel files are not accepted — save them as CSV UTF-8 first. The response lists every row as created, skipped, or failed; one bad row does not stop the others.

Stats & history

GET/api/v1/monitors/:id/stats— Aggregate statistics (JWT)
GET/api/v1/monitors/:id/results— Raw check-result history
GET/api/v1/monitors/:id/timeline— Status change timeline
GET/api/v1/monitors/:id/status-ranges— Run-length-encoded status ranges
GET/api/v1/monitors/:id/response-time-distribution— Response time percentiles (P50/P90/P95/P99)
GET/api/v1/monitors/:id/sla— SLA report (JWT)
GET/api/v1/monitors/:id/sla/export/csv— Export SLA as CSV (JWT)
GET/api/v1/monitors/:id/sla/export/pdf— Export SLA as PDF (JWT)

Diagnostics

GET/api/v1/monitors/:id/cert-chain— Live inspect TLS certificate chain
GET/api/v1/monitors/:id/dnsbl/provider-history— DNSBL per-provider history (JWT)
GET/api/v1/monitors/dnsbl-providers— Default DNSBL provider list

Alerts

Alert configuration.

GET/api/v1/alerts— List alerts
POST/api/v1/alerts— Create alert
PATCH/api/v1/alerts/:id— Update alert
DELETE/api/v1/alerts/:id— Delete alert
POST/api/v1/alerts/:id/test— Send test alert
POST/api/v1/alerts/:id/toggle— Toggle alert active/inactive
GET/api/v1/alerts/:id/history— Get alert delivery history
GET/api/v1/alerts/history— All alert delivery history (per workspace)
GET/api/v1/alerts/channels/availability— Available delivery channels (Telegram, Discord, Email, Slack, …)
POST/api/v1/alerts/test-send— Send a test message to an arbitrary channel (without saving an alert)

Incidents

Incident management.

GET/api/v1/incidents— List incidents
GET/api/v1/incidents/stats— Aggregate incident stats (total/ongoing/resolved/MTTR)
GET/api/v1/incidents/:id— Get incident details
PUT/api/v1/incidents/:id— Update incident
POST/api/v1/incidents/:id/updates— Add status update
POST/api/v1/incidents/:id/resolve— Resolve incident
DELETE/api/v1/incidents/:id— Delete incident

Status Pages

Public status pages.

GET/api/v1/status-pages— List status pages
POST/api/v1/status-pages— Create status page
GET/api/v1/status-pages/:id— Get status page
PUT/api/v1/status-pages/:id— Update status page
DELETE/api/v1/status-pages/:id— Delete status page
POST/api/v1/status-pages/:id/monitors— Add a monitor to the status page
DELETE/api/v1/status-pages/:id/monitors/:monitor_id— Remove a monitor from the status page
GET/api/v1/public/status/:slug— Public status (no auth)

Workspaces

Workspace management.

GET/api/v1/workspaces— List your workspaces
GET/api/v1/workspaces/default— Get your default workspace (fastest way to discover workspace_id)
GET/api/v1/workspaces/summary— Compact list (id, name) — handy for dropdowns
POST/api/v1/workspaces— Create workspace
PUT/api/v1/workspaces/:id— Update workspace
DELETE/api/v1/workspaces/:id— Delete workspace
POST/api/v1/workspaces/:id/set-default— Set default workspace
POST/api/v1/workspaces/:id/regenerate-ping-key— Rotate ping_key (existing heartbeat ping URLs stop working)
GET/api/v1/workspaces/:id/members— List workspace members (shared workspaces)
POST/api/v1/workspaces/:id/members— Invite a member by email (owner only)
PATCH/api/v1/workspaces/:id/members/:id— Change a member's role: editor or viewer (owner only)
DELETE/api/v1/workspaces/:id/members/:id— Remove a member (owner only)
POST/api/v1/workspaces/:id/invites— Create a one-time invite link, valid 14 days (owner only; works for users without an email)
POST/api/v1/workspaces/invites/accept— Accept an invite link and join the workspace
POST/api/v1/workspaces/:id/leave— Leave a shared workspace (members only; the owner cannot leave)

Heartbeats (Cron)

Cron job monitoring.

GET/api/v1/heartbeats— List heartbeat monitors
POST/api/v1/heartbeats— Create heartbeat monitor
GET/api/v1/heartbeats/:id— Get heartbeat monitor details
PATCH/api/v1/heartbeats/:id— Update heartbeat monitor
DELETE/api/v1/heartbeats/:id— Delete heartbeat monitor
POST/api/v1/heartbeats/:id/pause— Pause heartbeat monitor
POST/api/v1/heartbeats/:id/resume— Resume heartbeat monitor
POST/api/v1/heartbeats/:id/regenerate-secret— Regenerate authentication secret
GET/api/v1/heartbeats/:id/stats— Get heartbeat statistics (uptime, ping counts)
GET/api/v1/heartbeats/:id/pings— Get ping history (paginated)
GET/api/v1/ping/:ping_key/:slug— Record ping via GET (no auth, for simple cron jobs)
POST/api/v1/ping/:ping_key/:slug— Record ping via POST (no auth, with optional payload)
POST/api/v1/ping/:ping_key/:slug/start— Record job start signal (no auth)
POST/api/v1/ping/:ping_key/:slug/success— Record job success signal (no auth)
POST/api/v1/ping/:ping_key/:slug/fail— Record job failure signal (no auth)

API Keys

API key management.

GET/api/v1/api-keys— List API keys
POST/api/v1/api-keys— Create API key
DELETE/api/v1/api-keys/:id— Revoke API key

Maintenance

Maintenance windows.

GET/api/v1/maintenance— List maintenance windows
POST/api/v1/maintenance— Create maintenance window
GET/api/v1/maintenance/:id— Get maintenance window details
PUT/api/v1/maintenance/:id— Update maintenance window
DELETE/api/v1/maintenance/:id— Delete maintenance window
POST/api/v1/maintenance/:id/cancel— Cancel maintenance

Monitor Groups

Group management (12 endpoints, grouped by purpose).

Group CRUD

GET/api/v1/monitor-groups— List monitor groups
POST/api/v1/monitor-groups— Create monitor group
GET/api/v1/monitor-groups/:id— Get monitor group details
PUT/api/v1/monitor-groups/:id— Update group (name, description, paused flag)
DELETE/api/v1/monitor-groups/:id— Delete monitor group

Memberships (monitors & tags)

POST/api/v1/monitor-groups/:id/monitors— Add monitors to group
DELETE/api/v1/monitor-groups/:id/monitors— Remove monitors from group
POST/api/v1/monitor-groups/:id/tags— Add tags
DELETE/api/v1/monitor-groups/:id/tags— Remove tags

Status & stats

GET/api/v1/monitor-groups/stats— Aggregate stats across all groups
GET/api/v1/monitor-groups/:id/status— Get aggregate group status
PUT/api/v1/monitor-groups/:id/status— Update aggregate group status

Telegram Groups

Telegram group alerts.

GET/api/v1/telegram-groups— List linked Telegram groups
POST/api/v1/telegram-groups/link— Generate deep-link URL for Telegram group
DELETE/api/v1/telegram-groups/:id— Unlink Telegram group

Scheduled Reports

Automated email reports.

GET/api/v1/scheduled-reports— List scheduled reports
POST/api/v1/scheduled-reports— Create scheduled report
GET/api/v1/scheduled-reports/:id— Get report details
PUT/api/v1/scheduled-reports/:id— Update scheduled report
DELETE/api/v1/scheduled-reports/:id— Delete scheduled report
POST/api/v1/scheduled-reports/:id/pause— Pause report
POST/api/v1/scheduled-reports/:id/resume— Resume report
POST/api/v1/scheduled-reports/:id/run— Run report manually
POST/api/v1/scheduled-reports/:id/toggle— Toggle active flag (pause/resume in one call)
GET/api/v1/scheduled-reports/:id/runs— Report execution history

Alert Channel Configs

Channel credentials (SMTP, Twilio, etc.).

GET/api/v1/alert-channel-configs— List channel configurations
POST/api/v1/alert-channel-configs— Create channel config
PATCH/api/v1/alert-channel-configs/:id— Update channel config
DELETE/api/v1/alert-channel-configs/:id— Delete channel config
POST/api/v1/alert-channel-configs/:id/test— Send a test message via this config
POST/api/v1/alert-channel-configs/:id/activate— Activate (re-enable after auto-disable from a delivery error streak)
POST/api/v1/alert-channel-configs/:id/deactivate— Temporarily disable without deleting
GET/api/v1/alert-channel-configs/channels/:channel— Active config for a channel (telegram, discord, slack, …)

Dashboard

Aggregated statistics.

GET/api/v1/dashboard/stats— Aggregated dashboard statistics
GET/api/v1/dashboard/uptime-summary— Overall uptime summary across all monitors
GET/api/v1/dashboard/alerts-summary— Alerts summary
GET/api/v1/dashboard/incidents— Incidents summary
GET/api/v1/dashboard/combined— All dashboard data in one call

Webhooks (Outgoing)

Event webhook delivery (12 endpoints, grouped by purpose).

Webhook CRUD

GET/api/v1/webhooks— List webhooks
POST/api/v1/webhooks— Create webhook
GET/api/v1/webhooks/:id— Get webhook details
PATCH/api/v1/webhooks/:id— Update webhook
DELETE/api/v1/webhooks/:id— Delete webhook

Actions

POST/api/v1/webhooks/:id/test— Send a test payload
POST/api/v1/webhooks/:id/secret— Rotate HMAC signing secret (existing signatures stop being accepted on your side)

Deliveries & DLQ

GET/api/v1/webhooks/:id/deliveries— Delivery history
GET/api/v1/webhooks/:id/stats— Aggregate delivery statistics (success/error/p50/p95)
GET/api/v1/webhooks/:id/dlq— Dead Letter Queue: deliveries that failed all retries
POST/api/v1/webhooks/:id/dlq/:dlq_id/retry— Retry a DLQ entry
POST/api/v1/webhooks/:id/dlq/:dlq_id/resolve— Mark a DLQ entry resolved (without retrying)

Reference

GET/api/v1/webhooks/events— Available event types (used when creating a webhook)

Webhook Checks (Incoming)

Accept incoming webhooks from external services (10 endpoints + 1 public receiver, grouped by purpose).

Check CRUD

GET/api/v1/webhook-checks— List incoming webhook checks
POST/api/v1/webhook-checks— Create incoming webhook check
GET/api/v1/webhook-checks/:id— Get webhook check details
PUT/api/v1/webhook-checks/:id— Update webhook check
DELETE/api/v1/webhook-checks/:id— Delete webhook check

Check operations

POST/api/v1/webhook-checks/:id/pause— Pause webhook check
POST/api/v1/webhook-checks/:id/resume— Resume webhook check
POST/api/v1/webhook-checks/:id/regenerate-secret— Rotate secret (the existing HMAC signature from the source stops being accepted)

Events & stats

GET/api/v1/webhook-checks/:id/events— Incoming webhook check events
GET/api/v1/webhook-checks/:id/stats— Aggregate stats for an incoming check

Public receiver (no auth)

POST/api/v1/webhook-check/:slug— Record a delivery (this URL is what you hand to the external service)

Feedback

User feedback and feature requests.

POST/api/v1/feedback— Submit feedback
GET/api/v1/feedback— List feedback
GET/api/v1/feedback/:id— Get feedback details
PUT/api/v1/feedback/:id/status— Update feedback status (admin)
DELETE/api/v1/feedback/:id— Delete feedback (admin)

Web Push

Browser push notifications.

GET/api/v1/web-push/vapid-public-key— Get VAPID public key
GET/api/v1/web-push/subscriptions— List push subscriptions for the current user
POST/api/v1/web-push/subscribe— Subscribe to push notifications
POST/api/v1/web-push/unsubscribe— Unsubscribe from push notifications

Probe

Probe identification and IP addresses.

GET/api/v1/probe/ips— Get probe IP addresses (public, no auth)

Request Examples

Detailed cURL examples.

List monitors

curl -H "X-API-Key: YOUR_API_KEY" \
  https://pingzen.dev/api/v1/monitors?workspace_id=1

How to find your workspace_id

The workspace_id field is required in the payload when creating monitors, alerts, reports, and status pages. There are two ways to get it.

Option 1: via UI (fastest). Open the workspaces page — each workspace card shows its ID. For most users it’s 1.

Option 2: via API (for CI/scripts). The /api/v1/workspaces* endpoints currently require JWT, not X-API-Key (see “Where X-API-Key works” above). Steps:

# 1. Log in — get an access_token
ACCESS=$(curl -sS -X POST https://pingzen.dev/api/v1/auth/login \
  -H 'Content-Type: application/json' \
  -d '{"email":"you@example.com","password":"…"}' \
  | python3 -c 'import json,sys; print(json.load(sys.stdin)["access_token"])')

# 2. Default workspace (the one pre-selected in the UI)
curl -sS -H "Authorization: Bearer $ACCESS" \
  https://pingzen.dev/api/v1/workspaces/default
# → {"id": 42, "name": "My Workspace", "is_default": true, ...}

# 3. Or a compact list of all (id, name) for dropdowns
curl -sS -H "Authorization: Bearer $ACCESS" \
  https://pingzen.dev/api/v1/workspaces/summary

One-liner “log in → fetch default id” for CI:

ACCESS=$(curl -sS -X POST https://pingzen.dev/api/v1/auth/login \
  -H 'Content-Type: application/json' \
  -d "{\"email\":\"$EMAIL\",\"password\":\"$PASS\"}" \
  | python3 -c 'import json,sys; print(json.load(sys.stdin)["access_token"])')

WORKSPACE_ID=$(curl -sS -H "Authorization: Bearer $ACCESS" \
  https://pingzen.dev/api/v1/workspaces/default \
  | python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])')

echo "$WORKSPACE_ID"   # → 42

Then plug $WORKSPACE_ID into the POST /api/v1/monitors payload below (that one is an X-API-Key endpoint).

Create a monitor

curl -X POST https://pingzen.dev/api/v1/monitors \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "My Site", "url": "https://example.com", "protocol": "https", "workspace_id": 1, "interval_seconds": 60}'

Get monitor

curl -H "X-API-Key: YOUR_API_KEY" \
  https://pingzen.dev/api/v1/monitors/123

Update monitor

curl -X PATCH https://pingzen.dev/api/v1/monitors/123 \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"interval_seconds": 60, "name": "Updated Name"}'

Minimum interval_seconds depends on plan: FREE — 60s, paid — 30s. A value below the plan minimum returns 422 with a descriptive error.

Delete monitor

curl -X DELETE https://pingzen.dev/api/v1/monitors/123 \
  -H "X-API-Key: YOUR_API_KEY"

Create a heartbeat monitor

Create a heartbeat monitor for cron jobs and scheduled tasks. Returns a unique ping URL and secret. /heartbeats CRUD currently requires JWT (see “Where X-API-Key works”); workspace_id is passed as a query parameter, not in the body:

curl -X POST "https://pingzen.dev/api/v1/heartbeats?workspace_id=$WORKSPACE_ID" \
  -H "Authorization: Bearer $ACCESS" \
  -H "Content-Type: application/json" \
  -d '{"name": "Daily Backup", "expected_interval_seconds": 86400, "grace_period_seconds": 600}'

Send heartbeat ping (GET)

Simplest integration — just add to crontab or end of script. No authentication required.

# Simplest: chain with && so ping fires only on success
0 3 * * * /usr/local/bin/backup.sh && curl -fsS -m 10 https://pingzen.dev/api/v1/ping/YOUR_PING_KEY/YOUR_SLUG

# Or just a standalone ping
curl https://pingzen.dev/api/v1/ping/aBcDeFgHiJkLmNoPqR/daily-backup

Heartbeat lifecycle signals

Report job lifecycle signals (start/success/fail) with optional payload and duration. Use X-Secret header for authentication.

# Report success with payload and duration
curl -X POST https://pingzen.dev/api/v1/ping/aBcDeFgHiJkLmNoPqR/daily-backup/success \
  -H "X-Secret: YOUR_SECRET" \
  -H "Content-Type: application/json" \
  -d '{"duration_ms": 45000, "payload": {"rows": 500000}}'

# Report failure — triggers instant alert (no waiting for missed interval)
curl -X POST https://pingzen.dev/api/v1/ping/aBcDeFgHiJkLmNoPqR/daily-backup/fail \
  -H "X-Secret: YOUR_SECRET" \
  -H "Content-Type: application/json" \
  -d '{"payload": {"error": "disk full"}}'

# One-liner for cron: success OR fail
/usr/local/bin/backup.sh \
  && curl -fsS -m 10 https://pingzen.dev/api/v1/ping/aBcDeFgHiJkLmNoPqR/daily-backup/success \
  || curl -fsS -m 10 https://pingzen.dev/api/v1/ping/aBcDeFgHiJkLmNoPqR/daily-backup/fail

Code Examples

Python Example

import requests

# Get your API key from: Profile → API Keys → Create New Key
PINGZEN_API_KEY = "YOUR_API_KEY"
BASE_URL = "https://pingzen.dev/api/v1"

headers = {
    "X-API-Key": PINGZEN_API_KEY,
    "Content-Type": "application/json"
}

# Create a monitor
monitor_data = {
    "name": "My Website",
    "url": "https://example.com",
    "protocol": "https",
    "interval_seconds": 60,
    "workspace_id": 1
}

response = requests.post(
    f"{BASE_URL}/monitors",
    headers=headers,
    json=monitor_data
)

print(response.json())

# Get all monitors (response is {monitors: [...], total, page, page_size})
data = requests.get(
    f"{BASE_URL}/monitors",
    headers=headers,
    params={"workspace_id": 1},  # scope to your workspace
).json()

for monitor in data["monitors"]:
    print(f"{monitor['name']}: {monitor['status']}")

JavaScript Example

// Get your API key from: Profile → API Keys → Create New Key
const PINGZEN_API_KEY = 'pz_abc12345_...';
const BASE_URL = 'https://pingzen.dev/api/v1';

const headers = {
  'X-API-Key': PINGZEN_API_KEY,
  'Content-Type': 'application/json'
};

// Create a monitor
async function createMonitor() {
  const response = await fetch(`${BASE_URL}/monitors`, {
    method: 'POST',
    headers: headers,
    body: JSON.stringify({
      name: 'My Website',
      url: 'https://example.com',
      protocol: 'https',
      interval_seconds: 60,
      workspace_id: 1
    })
  });

  const data = await response.json();
  console.log(data);
}

// Get all monitors (response is {monitors: [...], total, page, page_size})
async function getMonitors() {
  const response = await fetch(
    `${BASE_URL}/monitors?workspace_id=1`,  // scope to your workspace
    { headers: headers },
  );

  const data = await response.json();
  data.monitors.forEach(monitor => {
    console.log(`${monitor.name}: ${monitor.status}`);
  });
}

createMonitor();
getMonitors();

Heartbeat / Cron Monitoring Examples

Monitor cron jobs, scheduled tasks, and background processes. Create a heartbeat monitor, then send pings from your scripts to confirm they ran.

Bash / Cron Job

#!/bin/bash
# === Pattern 1: Chain with && (recommended for cron) ===
# curl runs ONLY if backup succeeds (exit code 0)
# If backup fails — no ping — PingZen alerts after grace period
#
# 0 2 * * * /usr/local/bin/backup.sh && curl -fsS -m 10 https://pingzen.dev/api/v1/ping/YOUR_PING_KEY/YOUR_SLUG

# === Pattern 2: Report both success AND failure (best) ===
# /success on success, /fail on failure — instant alert, no waiting
#
# 0 2 * * * /usr/local/bin/backup.sh \
#   && curl -fsS -m 10 https://pingzen.dev/api/v1/ping/YOUR_PING_KEY/YOUR_SLUG/success \
#   || curl -fsS -m 10 https://pingzen.dev/api/v1/ping/YOUR_PING_KEY/YOUR_SLUG/fail

# === Pattern 3: Full script with duration and payload ===
PING_KEY="YOUR_PING_KEY"
SLUG="daily-backup"
SECRET="YOUR_SECRET"
PING_URL="https://pingzen.dev/api/v1/ping/$PING_KEY/$SLUG"
SECONDS=0  # Bash built-in timer

# Your backup logic
pg_dump mydb > /backups/mydb_$(date +%F).sql

if [ $? -eq 0 ]; then
  # Report success with duration
  curl -fsS -m 10 -X POST "$PING_URL/success" \
    -H "X-Secret: $SECRET" \
    -H "Content-Type: application/json" \
    -d "{\"duration_ms\": $((SECONDS * 1000))}"
else
  # Report failure — instant alert
  curl -fsS -m 10 -X POST "$PING_URL/fail" \
    -H "X-Secret: $SECRET" \
    -H "Content-Type: application/json" \
    -d "{\"payload\": {\"error\": \"pg_dump failed\"}}"
fi

Python Heartbeat

import requests

BASE_URL = "https://pingzen.dev/api/v1"

# /heartbeats CRUD currently requires JWT (X-API-Key not yet supported).
# Log in once and reuse the token; refresh via /auth/refresh after 15 minutes.
login = requests.post(
    f"{BASE_URL}/auth/login",
    json={"email": "you@example.com", "password": "..."},
).json()
ACCESS = login["access_token"]
WORKSPACE_ID = requests.get(
    f"{BASE_URL}/workspaces/default",
    headers={"Authorization": f"Bearer {ACCESS}"},
).json()["id"]

# Create a heartbeat monitor (workspace_id goes in the query, not the body)
heartbeat = requests.post(
    f"{BASE_URL}/heartbeats",
    params={"workspace_id": WORKSPACE_ID},
    headers={"Authorization": f"Bearer {ACCESS}", "Content-Type": "application/json"},
    json={
        "name": "Daily Backup",
        "expected_interval_seconds": 86400,
        "grace_period_seconds": 600,
    },
).json()

ping_key = heartbeat["ping_key"]  # Workspace secret key
slug = heartbeat["slug"]
secret = heartbeat["secret"]      # Save this — shown only once!
ping_url = f"{BASE_URL}/ping/{ping_key}/{slug}"
print(f"Ping URL: {ping_url}")

# Send a ping at the end of your script (no auth needed for the ping itself,
# X-Secret authenticates lifecycle signals)
requests.post(
    f"{ping_url}/success",
    headers={"X-Secret": secret, "Content-Type": "application/json"},
    json={"duration_ms": 12000, "payload": {"status": "ok"}},
)

JavaScript Heartbeat

const BASE_URL = 'https://pingzen.dev/api/v1';

// /heartbeats CRUD currently requires JWT (X-API-Key not yet supported).
// Login once, reuse the token; refresh via /auth/refresh after 15 minutes.
async function login(email, password) {
  const res = await fetch(`${BASE_URL}/auth/login`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ email, password }),
  });
  return (await res.json()).access_token;
}

async function defaultWorkspaceId(access) {
  const res = await fetch(`${BASE_URL}/workspaces/default`, {
    headers: { 'Authorization': `Bearer ${access}` },
  });
  return (await res.json()).id;
}

// Create a heartbeat monitor (workspace_id goes in the query, not the body)
async function createHeartbeat(access, workspaceId) {
  const response = await fetch(
    `${BASE_URL}/heartbeats?workspace_id=${workspaceId}`,
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${access}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        name: 'Nightly Report',
        expected_interval_seconds: 86400,
        grace_period_seconds: 600,
      }),
    },
  );

  const data = await response.json();
  console.log('Ping URL:', `${BASE_URL}/ping/${data.ping_key}/${data.slug}`);
  console.log('Secret:', data.secret); // Save — shown only once!
  return data;
}

// Send heartbeat ping from your cron job (no JWT needed; X-Secret authenticates)
async function sendPing(pingKey, slug, secret) {
  await fetch(`${BASE_URL}/ping/${pingKey}/${slug}/success`, {
    method: 'POST',
    headers: {
      'X-Secret': secret,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      duration_ms: 5000,
      payload: { rows_processed: 1000 },
    }),
  });
}

OpenAPI & AI Agents

PingZen provides machine-readable specifications for AI agent integration and automation tools.

Rate Limits

PlanRequests/HourBurst Limit
Free100/hour10/minute