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"
}
| Status | Meaning |
|---|---|
400 | Invalid request parameters |
401 | Missing or invalid API key |
403 | Plan doesn't include API access, or check limit reached |
404 | Resource not found |
429 | Rate limit exceeded (see Rate Limiting) |
500 | Internal 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:
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests per window (60) |
X-RateLimit-Remaining | Requests remaining in current window |
Retry-After | Seconds 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
| Field | Type | Default | Description |
|---|---|---|---|
name | string | "Unnamed Check" | Name for the check |
period | integer | 3600 | Expected interval in seconds (60 – 604800) |
grace | integer | 300 | Grace period in seconds (60 – 3600) |
tags | string or string[] | "" | Comma-separated tags or array, e.g. "production,database" |
maint_start | integer|null | null | One-time maintenance window start (Unix timestamp) |
maint_end | integer|null | null | One-time maintenance window end (Unix timestamp) |
maint_schedule | string | "" | 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).
| Schedule | Meaning |
|---|---|
daily:02:00-04:00 | Every day 2:00–4:00 UTC |
weekdays:03:00-05:00 | Mon–Fri 3:00–5:00 UTC |
weekends:00:00-06:00 | Sat–Sun 0:00–6:00 UTC |
sun:02:00-06:00 | Every Sunday 2:00–6:00 UTC |
mon,wed,fri:04:00-05:00 | Mon, 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
| Param | Type | Default | Description |
|---|---|---|---|
limit | integer | 50 | Max results (1 – 200) |
offset | integer | 0 | Pagination 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
| Param | Type | Default | Description |
|---|---|---|---|
limit | integer | 50 | Max results (1 – 200) |
offset | integer | 0 | Pagination 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 -->

<!-- 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.
| Status | Color |
|---|---|
up | Green (#22c55e) |
down | Red (#ef4444) |
late | Amber (#f59e0b) |
paused | Gray (#6b7280) |
new | Blue (#3b82f6) |
Uptime Badge
GET /badge/:checkId/uptime
Returns an SVG badge showing the uptime percentage for a check over a given period.
Query Parameters
| Param | Type | Default | Description |
|---|---|---|---|
period | string | 24h | Time window: 24h, 7d, or 30d |
Example
<!-- 24-hour uptime -->

<!-- 7-day uptime -->

<!-- 30-day uptime -->

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
- Go to Dashboard → Settings and click Generate Signing Secret
- Copy the
whsec_...secret and store it securely in your application - Verify the
X-CronPulse-Signatureheader 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:
| Retry | Delay | Timeout |
|---|---|---|
| 1st retry | ~30 seconds | 10s |
| 2nd retry | ~2 minutes | 10s |
| 3rd retry | ~8 minutes | 10s |
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.