Heartbeats API
Heartbeat monitors work in reverse compared to regular monitors. Instead of Sentro checking your endpoints, your service pings Sentro at regular intervals. If Sentro does not receive a ping within the expected timeframe (plus a configurable grace period), it creates an incident and sends alerts through your configured channels.
This is ideal for monitoring:
- Cron jobs -- Ensure your scheduled tasks actually run on time
- Background workers -- Verify queue processors and async jobs are alive
- Batch processes -- Confirm nightly ETL pipelines, backups, and data syncs complete
- Scheduled reports -- Make sure report generation finishes within its window
How It Works
- Create a heartbeat via the API or dashboard -- you get a unique ping URL
- Add a ping call to the end of your job (e.g.,
curl https://app.sentro.dev/api/heartbeat/{slug}/ping) - Sentro watches for pings -- if one is missed beyond the grace period, an alert fires
- When pings resume, the incident is automatically resolved
Heartbeat Object
{
"id": "hbt_abc123def456",
"orgId": "org_abc123",
"name": "Nightly Database Backup",
"slug": "nightly-database-backup-a1b2c3",
"pingUrl": "https://app.sentro.dev/api/heartbeat/nightly-database-backup-a1b2c3/ping",
"expectedIntervalSeconds": 86400,
"graceSeconds": 300,
"status": "ACTIVE",
"isUp": true,
"lastPingAt": "2025-01-15T02:05:12.000Z",
"nextExpectedAt": "2025-01-16T02:05:12.000Z",
"createdAt": "2025-01-01T00:00:00.000Z",
"updatedAt": "2025-01-15T02:05:12.000Z"
}
| Field | Type | Description |
|---|---|---|
id | string | Unique heartbeat identifier |
orgId | string | Organization that owns the heartbeat |
name | string | Human-readable heartbeat name (1-100 characters) |
slug | string | Auto-generated unique slug used in the ping URL |
pingUrl | string | The URL your service should ping: /api/heartbeat/{slug}/ping |
expectedIntervalSeconds | number | Expected time between pings in seconds (30-86400) |
graceSeconds | number | Grace period before marking as missed in seconds (0-3600) |
status | string | ACTIVE or PAUSED |
isUp | boolean | Whether the heartbeat is receiving pings on time |
lastPingAt | string|null | ISO 8601 timestamp of the last received ping |
nextExpectedAt | string|null | ISO 8601 timestamp when the next ping is expected |
createdAt | string | ISO 8601 creation timestamp |
updatedAt | string | ISO 8601 last update timestamp |
List Heartbeats
GET /v1/heartbeats
Returns all heartbeat monitors belonging to your organization.
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | number | 50 | Number of heartbeats to return (max 200) |
offset | number | 0 | Number of heartbeats to skip for pagination |
status | string | -- | Filter by status: ACTIVE or PAUSED |
Example Request
curl "https://api.sentro.dev/v1/heartbeats?limit=10&status=ACTIVE" \
-H "Authorization: Bearer sentro_sk_live_your_key_here"
Response 200 OK
{
"data": [
{
"id": "hbt_abc123def456",
"orgId": "org_abc123",
"name": "Nightly Database Backup",
"slug": "nightly-database-backup-a1b2c3",
"pingUrl": "https://app.sentro.dev/api/heartbeat/nightly-database-backup-a1b2c3/ping",
"expectedIntervalSeconds": 86400,
"graceSeconds": 300,
"status": "ACTIVE",
"isUp": true,
"lastPingAt": "2025-01-15T02:05:12.000Z",
"nextExpectedAt": "2025-01-16T02:05:12.000Z",
"createdAt": "2025-01-01T00:00:00.000Z",
"updatedAt": "2025-01-15T02:05:12.000Z"
},
{
"id": "hbt_ghi789jkl012",
"orgId": "org_abc123",
"name": "Hourly Report Generator",
"slug": "hourly-report-generator-d4e5f6",
"pingUrl": "https://app.sentro.dev/api/heartbeat/hourly-report-generator-d4e5f6/ping",
"expectedIntervalSeconds": 3600,
"graceSeconds": 60,
"status": "ACTIVE",
"isUp": true,
"lastPingAt": "2025-01-15T09:00:45.000Z",
"nextExpectedAt": "2025-01-15T10:00:45.000Z",
"createdAt": "2025-01-05T12:00:00.000Z",
"updatedAt": "2025-01-15T09:00:45.000Z"
}
],
"meta": {
"total": 2
}
}
Fetch all heartbeat monitors in your organization.
/v1/heartbeats?limit=50&offset=0Get Heartbeat
GET /v1/heartbeats/:id
Returns a single heartbeat monitor with its most recent pings.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | string | The heartbeat ID |
Response 200 OK
{
"data": {
"id": "hbt_abc123def456",
"orgId": "org_abc123",
"name": "Nightly Database Backup",
"slug": "nightly-database-backup-a1b2c3",
"pingUrl": "https://app.sentro.dev/api/heartbeat/nightly-database-backup-a1b2c3/ping",
"expectedIntervalSeconds": 86400,
"graceSeconds": 300,
"status": "ACTIVE",
"isUp": true,
"lastPingAt": "2025-01-15T02:05:12.000Z",
"nextExpectedAt": "2025-01-16T02:05:12.000Z",
"createdAt": "2025-01-01T00:00:00.000Z",
"updatedAt": "2025-01-15T02:05:12.000Z",
"recentPings": [
{
"id": "ping_001",
"ipAddress": "203.0.113.42",
"pingAt": "2025-01-15T02:05:12.000Z"
},
{
"id": "ping_002",
"ipAddress": "203.0.113.42",
"pingAt": "2025-01-14T02:04:58.000Z"
},
{
"id": "ping_003",
"ipAddress": "203.0.113.42",
"pingAt": "2025-01-13T02:05:03.000Z"
}
]
}
}
Error
{
"error": {
"code": "NOT_FOUND",
"message": "Heartbeat with id 'hbt_invalid' not found"
}
}
Get a single heartbeat monitor with its recent pings.
/v1/heartbeats/:idCreate Heartbeat
POST /v1/heartbeats
Creates a new heartbeat monitor. A unique slug and pingUrl are generated automatically. The heartbeat starts in ACTIVE status, waiting for its first ping.
This endpoint requires a READ_WRITE API key. Read-only keys will receive a 403 Forbidden response.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Heartbeat name (1-100 characters) |
expectedIntervalSeconds | number | No | Expected time between pings in seconds (30-86400, default: 300) |
graceSeconds | number | No | Grace period before alerting in seconds (0-3600, default: 60) |
Example Request
curl -X POST https://api.sentro.dev/v1/heartbeats \
-H "Authorization: Bearer sentro_sk_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"name": "Nightly Database Backup",
"expectedIntervalSeconds": 86400,
"graceSeconds": 300
}'
Response 201 Created
{
"data": {
"id": "hbt_new456def789",
"orgId": "org_abc123",
"name": "Nightly Database Backup",
"slug": "nightly-database-backup-a1b2c3",
"pingUrl": "https://app.sentro.dev/api/heartbeat/nightly-database-backup-a1b2c3/ping",
"expectedIntervalSeconds": 86400,
"graceSeconds": 300,
"status": "ACTIVE",
"isUp": true,
"lastPingAt": null,
"nextExpectedAt": null,
"createdAt": "2025-01-15T10:00:00.000Z",
"updatedAt": "2025-01-15T10:00:00.000Z"
}
}
Validation Error
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid heartbeat data",
"details": {
"name": ["Name is required (1-100 characters)"],
"expectedIntervalSeconds": ["Must be between 30 and 86400"]
}
}
}
Plan Limit Error
{
"error": {
"code": "PLAN_LIMIT",
"message": "You have reached the heartbeat limit for your plan. Upgrade to add more heartbeat monitors."
}
}
Create a new heartbeat monitor with an auto-generated ping URL.
/v1/heartbeatsUpdate Heartbeat
PATCH /v1/heartbeats/:id
Updates an existing heartbeat monitor. Only the fields you include in the request body will be updated.
This endpoint requires a READ_WRITE API key. Read-only keys will receive a 403 Forbidden response.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | string | The heartbeat ID |
Request Body
All fields are optional. Include only the fields you want to change.
| Field | Type | Description |
|---|---|---|
name | string | Updated heartbeat name (1-100 characters) |
expectedIntervalSeconds | number | Updated expected interval (30-86400) |
graceSeconds | number | Updated grace period (0-3600) |
status | string | Set to ACTIVE or PAUSED |
Example Request
curl -X PATCH https://api.sentro.dev/v1/heartbeats/hbt_abc123def456 \
-H "Authorization: Bearer sentro_sk_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"expectedIntervalSeconds": 43200,
"graceSeconds": 600
}'
Response 200 OK
{
"data": {
"id": "hbt_abc123def456",
"orgId": "org_abc123",
"name": "Nightly Database Backup",
"slug": "nightly-database-backup-a1b2c3",
"pingUrl": "https://app.sentro.dev/api/heartbeat/nightly-database-backup-a1b2c3/ping",
"expectedIntervalSeconds": 43200,
"graceSeconds": 600,
"status": "ACTIVE",
"isUp": true,
"lastPingAt": "2025-01-15T02:05:12.000Z",
"nextExpectedAt": "2025-01-15T14:05:12.000Z",
"createdAt": "2025-01-01T00:00:00.000Z",
"updatedAt": "2025-01-15T10:30:00.000Z"
}
}
Update specific fields on an existing heartbeat monitor.
/v1/heartbeats/:idDelete Heartbeat
DELETE /v1/heartbeats/:id
Permanently deletes a heartbeat monitor and all its associated ping history.
This endpoint requires a READ_WRITE API key. Read-only keys will receive a 403 Forbidden response.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | string | The heartbeat ID |
Response 200 OK
{
"data": {
"id": "hbt_abc123def456",
"deleted": true
}
}
Error
{
"error": {
"code": "NOT_FOUND",
"message": "Heartbeat with id 'hbt_invalid' not found"
}
}
Permanently delete a heartbeat monitor and all its ping history.
/v1/heartbeats/:idSending Pings
To report that your job completed successfully, send any HTTP request (GET, POST, or HEAD) to the heartbeat's pingUrl:
# Using curl (add this to the end of your cron job or script)
curl -fsS --retry 3 https://app.sentro.dev/api/heartbeat/nightly-database-backup-a1b2c3/ping
The ping endpoint accepts any HTTP method and returns 200 OK with a JSON body:
{
"ok": true,
"heartbeat": "Nightly Database Backup",
"receivedAt": "2025-01-15T02:05:12.000Z"
}
Use curl -fsS --retry 3 to silently retry failed pings up to 3 times. This ensures transient network issues do not cause false missed-ping alerts.
Ping Object
Pings are included in the recentPings array when fetching a single heartbeat via GET /v1/heartbeats/:id.
| Field | Type | Description |
|---|---|---|
id | string | Unique ping identifier |
ipAddress | string | IP address of the client that sent the ping |
pingAt | string | ISO 8601 timestamp when the ping was received |
Plan Limits
The number of heartbeat monitors you can create depends on your subscription plan:
| Plan | Heartbeat Limit |
|---|---|
| Free | 3 |
| Starter | 10 |
| Pro | 50 |
| Max | Unlimited |
Attempting to create a heartbeat beyond your plan limit returns a 402 error with code PLAN_LIMIT.
Common Patterns
Monitor a Cron Job
Add a ping to the end of your crontab entry:
# Run backup every night at 2 AM, then ping Sentro
0 2 * * * /usr/local/bin/backup.sh && curl -fsS --retry 3 https://app.sentro.dev/api/heartbeat/nightly-backup-a1b2c3/ping
The && operator ensures the ping is only sent if the backup script exits successfully (exit code 0). If the script fails, no ping is sent and Sentro triggers an alert after the grace period.
Monitor a Node.js Worker
const HEARTBEAT_URL =
"https://app.sentro.dev/api/heartbeat/queue-worker-x9y8z7/ping";
async function processQueue() {
while (true) {
const jobs = await queue.drain();
console.log(`Processed ${jobs.length} jobs`);
// Report health to Sentro after each batch
await fetch(HEARTBEAT_URL, { method: "POST" }).catch(() => {});
await sleep(60_000); // Wait 1 minute before next batch
}
}
Monitor a Python Script
import requests
def run_etl_pipeline():
extract_data()
transform_data()
load_data()
# Signal success to Sentro
requests.get(
"https://app.sentro.dev/api/heartbeat/etl-pipeline-k3m4n5/ping",
timeout=10
)
if __name__ == "__main__":
run_etl_pipeline()
Pause and Resume
Temporarily disable a heartbeat during planned maintenance to avoid false alerts:
# Pause before maintenance
curl -X PATCH https://api.sentro.dev/v1/heartbeats/hbt_abc123def456 \
-H "Authorization: Bearer sentro_sk_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{"status": "PAUSED"}'
# ... perform maintenance ...
# Resume monitoring
curl -X PATCH https://api.sentro.dev/v1/heartbeats/hbt_abc123def456 \
-H "Authorization: Bearer sentro_sk_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{"status": "ACTIVE"}'