Documentation

Monitor your cron jobs using the CLI or REST API.

CLI — Install

Install the CronPulse CLI globally:

npm install -g cron-pulse-cli

Or use directly without installing:

npx cron-pulse-cli ping YOUR_CHECK_ID

CLI — Send a Ping

Send a heartbeat ping to signal your job ran successfully:

# Success ping (default)
cronpulse ping YOUR_CHECK_ID

# Start signal — mark job as "running"
cronpulse ping YOUR_CHECK_ID --start

# Fail signal — immediately trigger alert
cronpulse ping YOUR_CHECK_ID --fail

In a crontab

*/5 * * * * cronpulse ping abc123

CLI — Wrap a Command

Wrap any command to automatically send start/success/fail signals:

# Sends /start, runs your command, then /ping or /fail based on exit code
cronpulse wrap YOUR_CHECK_ID -- ./backup.sh

# Works with pipes and redirects
cronpulse wrap YOUR_CHECK_ID -- pg_dump mydb > /tmp/backup.sql

# In a crontab
0 2 * * * cronpulse wrap abc123 -- ./nightly-backup.sh

Exit code 0 → success ping. Any other exit code → fail signal (immediate alert).

CLI — List Checks

List all your checks (requires API key):

# First configure your API key
cronpulse config --api-key YOUR_API_KEY

# List all checks
cronpulse list

# Filter by tag or group
cronpulse list --tag production
cronpulse list --group database

# Show check details
cronpulse status abc123

CLI — Configuration

# Set your API key (get from Dashboard → Settings)
cronpulse config --api-key cpk_your_key_here

# Use a self-hosted server
cronpulse config --server https://my-cronpulse.example.com

# View current config
cronpulse config

Config is stored at ~/.cronpulse/config.json.

REST API — Base URL

https://cron-pulse.com/api/v1

All API endpoints are relative to this base URL.

Authentication

Authenticate by including your API key in the Authorization header:

Authorization: Bearer YOUR_API_KEY

Generate your API key in Dashboard → Settings. Your key is shown only once — store it securely.

API access requires a Pro or Business plan. Free and Starter plans can manage checks via the dashboard.

Error Responses

All errors return JSON with an error field:

{
  "error": "Description of what went wrong"
}
StatusMeaning
400Invalid request parameters
401Missing or invalid API key
403Plan doesn't include API access, or check limit reached
404Resource not found
429Rate limit exceeded (see Rate Limiting)
500Internal server error

Rate Limiting

The API enforces a rate limit of 60 requests per minute per authenticated user. Rate limit information is included in every response via headers:

HeaderDescription
X-RateLimit-LimitMaximum requests per window (60)
X-RateLimit-RemainingRequests remaining in current window
Retry-AfterSeconds until the rate limit resets (only on 429)

When the limit is exceeded, the API returns 429 Too Many Requests:

{
  "error": "Rate limit exceeded",
  "retry_after": 60
}

Note: The ping endpoint (/ping/:id) is not rate limited — your cron jobs can ping as frequently as needed.

List All Checks

GET /api/v1/checks

Returns all checks for the authenticated user, ordered by creation date (newest first).

curl -H "Authorization: Bearer YOUR_API_KEY" \
  https://cron-pulse.com/api/v1/checks

Response

{
  "checks": [
    {
      "id": "abc123",
      "name": "Nightly backup",
      "period": 86400,
      "grace": 300,
      "status": "up",
      "last_ping_at": 1707700000,
      "next_expected_at": 1707786400,
      "alert_count": 0,
      "ping_count": 42,
      "created_at": 1707000000,
      "updated_at": 1707700000
    }
  ]
}

Create a Check

POST /api/v1/checks

Request Body

FieldTypeDefaultDescription
namestring"Unnamed Check"Name for the check
periodinteger3600Expected interval in seconds (60 – 604800)
graceinteger300Grace period in seconds (60 – 3600)
tagsstring or string[]""Comma-separated tags or array, e.g. "production,database"
maint_startinteger|nullnullOne-time maintenance window start (Unix timestamp)
maint_endinteger|nullnullOne-time maintenance window end (Unix timestamp)
maint_schedulestring""Recurring maintenance schedule, e.g. "daily:02:00-04:00", "sun:02:00-06:00"

Maintenance Schedule Format

Recurring maintenance windows suppress alerts during scheduled maintenance. Format: day(s):HH:MM-HH:MM (UTC).

ScheduleMeaning
daily:02:00-04:00Every day 2:00–4:00 UTC
weekdays:03:00-05:00Mon–Fri 3:00–5:00 UTC
weekends:00:00-06:00Sat–Sun 0:00–6:00 UTC
sun:02:00-06:00Every Sunday 2:00–6:00 UTC
mon,wed,fri:04:00-05:00Mon, Wed, Fri 4:00–5:00 UTC
curl -X POST -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "Nightly backup", "period": 86400, "grace": 600, "tags": "production,database", "maint_schedule": "daily:02:00-04:00"}' \
  https://cron-pulse.com/api/v1/checks

Response 201

{
  "check": {
    "id": "abc123",
    "name": "Nightly backup",
    "period": 86400,
    "grace": 600,
    "status": "new",
    ...
  },
  "ping_url": "https://cron-pulse.com/ping/abc123"
}

Get a Check

GET /api/v1/checks/:id

curl -H "Authorization: Bearer YOUR_API_KEY" \
  https://cron-pulse.com/api/v1/checks/abc123

Response

{
  "check": { ... },
  "ping_url": "https://cron-pulse.com/ping/abc123"
}

Update a Check

PATCH /api/v1/checks/:id

Update name, period, or grace. Only include the fields you want to change.

curl -X PATCH -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "Daily DB backup", "grace": 900}' \
  https://cron-pulse.com/api/v1/checks/abc123

Response

{
  "check": { ... }
}

Delete a Check

DELETE /api/v1/checks/:id

curl -X DELETE -H "Authorization: Bearer YOUR_API_KEY" \
  https://cron-pulse.com/api/v1/checks/abc123

Response

{
  "deleted": true
}

Pause a Check

POST /api/v1/checks/:id/pause

Pauses monitoring. No alerts will fire while paused.

curl -X POST -H "Authorization: Bearer YOUR_API_KEY" \
  https://cron-pulse.com/api/v1/checks/abc123/pause

Response

{
  "paused": true
}

Resume a Check

POST /api/v1/checks/:id/resume

Resumes monitoring after a pause. Status resets to new until the next ping.

curl -X POST -H "Authorization: Bearer YOUR_API_KEY" \
  https://cron-pulse.com/api/v1/checks/abc123/resume

Response

{
  "resumed": true
}

Export Checks

GET /api/v1/checks/export

Export all checks as a JSON file. Useful for backup or migration.

curl -H "Authorization: Bearer YOUR_API_KEY" \
  https://cron-pulse.com/api/v1/checks/export

Response

{
  "version": 1,
  "exported_at": "2026-02-12T10:30:00.000Z",
  "checks": [
    {
      "name": "Nightly backup",
      "period": 86400,
      "grace": 300,
      "tags": "production,database"
    }
  ]
}

Import Checks

POST /api/v1/checks/import

Import checks from a JSON payload. Uses the same format as the export endpoint. Checks that exceed your plan limit will be skipped.

Request Body

{
  "checks": [
    {
      "name": "Nightly backup",
      "period": 86400,
      "grace": 300,
      "tags": "production,database"
    },
    {
      "name": "Hourly sync",
      "period": 3600,
      "grace": 120
    }
  ]
}
curl -X POST -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d @cronpulse-checks.json \
  https://cron-pulse.com/api/v1/checks/import

Response 201

{
  "imported": 2,
  "skipped": 0,
  "checks": [
    {
      "id": "abc123",
      "name": "Nightly backup",
      "ping_url": "https://cron-pulse.com/ping/abc123"
    },
    {
      "id": "def456",
      "name": "Hourly sync",
      "ping_url": "https://cron-pulse.com/ping/def456"
    }
  ]
}

Ping History

GET /api/v1/checks/:id/pings

ParamTypeDefaultDescription
limitinteger50Max results (1 – 200)
offsetinteger0Pagination offset
curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://cron-pulse.com/api/v1/checks/abc123/pings?limit=10"

Response

{
  "pings": [
    {
      "id": 1,
      "check_id": "abc123",
      "timestamp": 1707700000,
      "source_ip": "203.0.113.1",
      "duration": null,
      "type": "success"
    }
  ]
}

Alert History

GET /api/v1/checks/:id/alerts

ParamTypeDefaultDescription
limitinteger50Max results (1 – 200)
offsetinteger0Pagination offset
curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://cron-pulse.com/api/v1/checks/abc123/alerts?limit=10"

Response

{
  "alerts": [
    {
      "id": 1,
      "check_id": "abc123",
      "channel_id": "ch_001",
      "type": "down",
      "status": "sent",
      "error": null,
      "created_at": 1707700000,
      "sent_at": 1707700005
    }
  ]
}

Ping Endpoint

This is the public endpoint your cron jobs hit. No authentication required.

GET /ping/:id

Send a ping to signal that your cron job ran successfully. Accepts GET, POST, or HEAD.

# Add to the end of your cron job:
curl -fsS https://cron-pulse.com/ping/YOUR_CHECK_ID

Response

OK

Returns 200 OK on success, 404 if the check ID doesn't exist.

Status Badge

Embed a live status badge in your README, docs, or status page. No authentication required.

GET /badge/:checkId

Returns an SVG image showing the current status of a check: up, down, late, paused, or new.

Example

<!-- Markdown -->
![CronPulse](https://cron-pulse.com/badge/YOUR_CHECK_ID)

<!-- HTML -->
<img src="https://cron-pulse.com/badge/YOUR_CHECK_ID" alt="CronPulse status" />

Response

Returns image/svg+xml with 30-second cache. Returns 404 with a "not found" badge if the check ID doesn't exist.

StatusColor
upGreen (#22c55e)
downRed (#ef4444)
lateAmber (#f59e0b)
pausedGray (#6b7280)
newBlue (#3b82f6)

Uptime Badge

GET /badge/:checkId/uptime

Returns an SVG badge showing the uptime percentage for a check over a given period.

Query Parameters

ParamTypeDefaultDescription
periodstring24hTime window: 24h, 7d, or 30d

Example

<!-- 24-hour uptime -->
![Uptime](https://cron-pulse.com/badge/YOUR_CHECK_ID/uptime)

<!-- 7-day uptime -->
![Uptime 7d](https://cron-pulse.com/badge/YOUR_CHECK_ID/uptime?period=7d)

<!-- 30-day uptime -->
![Uptime 30d](https://cron-pulse.com/badge/YOUR_CHECK_ID/uptime?period=30d)

Response

Returns image/svg+xml with 60-second cache. Color coded: green (≥ 99.5%), amber (≥ 95%), red (< 95%).

Webhook Signature Verification

CronPulse signs all outgoing webhook notifications with HMAC-SHA256. This lets you verify that the payload was sent by CronPulse and hasn't been tampered with.

Setup

  1. Go to Dashboard → Settings and click Generate Signing Secret
  2. Copy the whsec_... secret and store it securely in your application
  3. Verify the X-CronPulse-Signature header on every incoming webhook

Verification

The X-CronPulse-Signature header contains a hex-encoded HMAC-SHA256 hash of the raw request body, signed with your webhook signing secret.

# Node.js verification example
const crypto = require('crypto');

function verifySignature(body, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(body)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

// In your webhook handler:
app.post('/webhook', (req, res) => {
  const signature = req.headers['x-cronpulse-signature'];
  const isValid = verifySignature(
    req.rawBody, signature, process.env.CRONPULSE_WEBHOOK_SECRET
  );
  if (!isValid) return res.status(401).send('Invalid signature');
  // Process the webhook...
});
# Python verification example
import hmac, hashlib

def verify_signature(body: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode(), body, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature)

Webhook Event Types

When a check changes status, CronPulse sends a POST request to your configured webhook URL with the following JSON payloads:

check.down — Check is overdue

{
  "event": "check.down",
  "check": {
    "id": "abc123",
    "name": "Nightly backup",
    "status": "down",
    "last_ping_at": 1707700000,
    "period": 86400
  },
  "timestamp": 1707786400
}

check.up — Check recovered

{
  "event": "check.up",
  "check": {
    "id": "abc123",
    "name": "Nightly backup"
  },
  "timestamp": 1707786500
}

test — Test notification

{
  "event": "test",
  "message": "This is a test notification from CronPulse.",
  "timestamp": 1707786600
}

All webhook requests include Content-Type: application/json and have a 5-second timeout. If you have a signing secret configured, the X-CronPulse-Signature header will also be included.

Webhook Automatic Retries

If a webhook or Slack notification fails (network error, non-2xx response), CronPulse automatically retries up to 3 times with exponential backoff:

RetryDelayTimeout
1st retry~30 seconds10s
2nd retry~2 minutes10s
3rd retry~8 minutes10s

Retry payloads include an additional retry field indicating the attempt number:

{
  "event": "check.down",
  "check": { ... },
  "timestamp": 1707786400,
  "retry": 1
}

Note: Email notifications are not retried by CronPulse as the email provider (Resend) handles its own retry logic. After 3 failed attempts, the alert is marked as permanently failed and visible in your incident timeline.