Quick Start
From zero to your first missed-run alert in under 5 minutes. Create your workspace and grab your API key from the dashboard — then follow these three steps.
Start with Authentication to set up your API key, then Register a Job to create your first monitored schedule, and finally Send Pings from your cron script.
Authentication
All API calls are authenticated with an API key. Grab yours from Settings → API Key in the dashboard. Keys are shown only once — store them securely.
API Key header
Pass the key in the X-Api-Key header on every request. Keys always start with sg_ followed by 48 hex characters.
curl -X POST https://api.syncguard.dev/api/v1/jobs/daily-report/ping \ -H "X-Api-Key: sg_a3f9d2c814e77b9f5a6d3e2c0b1f8e4d7a9c5b3" \ -H "Content-Type: application/json" \ -d '{ ... }'
SYNCGUARD_API_KEY=sg_...Register a Job
Before you can send pings, register the job in your workspace. You can do this through the Jobs page in the dashboard, or via the API.
Request fields
{ // Required "jobKey": "daily-report", // unique identifier for this job in your workspace "name": "Daily Report", // human-readable display name // Required — how often the job should run "expectedIntervalSeconds": 86400, // 24 hours // Optional — extra buffer before counting a miss (default: 60) "gracePeriodSeconds": 300, // 5 minutes // Optional — how many tenants must miss before incident opens (default: 3) "tenantImpactThreshold": 1, // Optional — consecutive misses per tenant before incident opens (default: 3) "consecutiveMissThreshold": 1 }
Response
{ "isSuccess": true, "data": { "id": "b2e3a1f4-7c8d-4e9f-a1b2-c3d4e5f6a7b8", "jobKey": "daily-report", "name": "Daily Report", "expectedIntervalSeconds": 86400, "gracePeriodSeconds": 300, "tenantImpactThreshold": 1, "consecutiveMissThreshold": 1, "isActive": true, "pingUrl": "https://api.syncguard.dev/api/v1/jobs/daily-report/ping" } }
pingUrl in the response is the exact URL you paste into your cron script. Copy it from the job detail page in the dashboard too.How thresholds work
An incident opens when either threshold is met first:
// consecutiveMissThreshold = 1 → alert on the very first miss per tenant // tenantImpactThreshold = 3 → alert when 3+ tenants each miss at least once // Example: job runs every hour, grace period 5 min // effectiveWindow = 3600 + 300 = 3905 seconds // missedRuns = floor((now - lastSuccessAt) / 3905)
Send Pings
POST /api/v1/jobs/{jobKey}/ping is the only endpoint your cron script needs to call. Send a ping after every run — SyncGuard tracks the last-seen time per tenant and opens an incident when the window expires.
Request fields
{ // Required — your customer's external ID (must exist as a tenant in your workspace) "tenantId": "acme-corp", // Required — one of: "success", "failure", "start" "status": "success" }
Ping statuses
successJob completed successfully. Resets missedRuns to 0. This is the most common ping.
failureJob ran but reported an explicit failure. Recorded but does not reset missedRuns.
startJob started. Optional — useful for tracking run duration. Does not affect miss detection.
Example — shell script
#!/bin/bash # Run the job python generate_daily_report.py --tenant acme-corp # Ping SyncGuard on success if [ $? -eq 0 ]; then curl -s -X POST https://api.syncguard.dev/api/v1/jobs/daily-report/ping \ -H "X-Api-Key: $SYNCGUARD_API_KEY" \ -H "Content-Type: application/json" \ -d '{"tenantId":"acme-corp","status":"success"}' fi
Idempotency
Duplicate success pings from the same tenant within 10 seconds are automatically deduplicated — safe to retry without side effects.
Error responses
// 404 — job key not found in this workspace { "isSuccess": false, "message": "Job not found." } // 404 — tenantId not registered as a tenant { "isSuccess": false, "message": "Tenant not found." } // 422 — tenant exists but is deactivated { "isSuccess": false, "message": "Tenant is deactivated." }
Incidents
SyncGuard opens, manages, and closes incidents automatically — your code doesn't need to manage them. This section explains the lifecycle so you understand what SyncGuard is doing with your pings.
Miss detection
Every 60 seconds, SyncGuard evaluates all active jobs. For each tenant, it computes:
missedRuns = floor((now - lastSuccessAt) / (expectedIntervalSeconds + gracePeriodSeconds)) // If missedRuns >= consecutiveMissThreshold → tenant is "affected" // If affectedTenantCount >= tenantImpactThreshold → incident opens
Lifecycle
// Open → miss threshold met, incident opened, alert fired // Recovering → no new misses detected for RecoveryWindow (default 5 min) // Closed → stable during CloseWindow (default 10 min), resolved alert fired // Reopened → misses resume during Recovering → back to Open (clock resets) // Chronic → incident has been reopened 3+ times (isChronic: true)
View full incident details, timelines, and affected tenants in the Incidents section of your dashboard.
Alerts
SyncGuard fires alerts automatically at each lifecycle transition. Configure Slack, PagerDuty, OpsGenie, or Teams once from your workspace settings — no per-job wiring needed in your code.
Incident openedMiss threshold met — job is overdue for one or more tenants
Incident reopenedMisses resume after entering Recovering state
Incident resolvedJob is pinging again and stable for the CloseWindow
Blast radius expandedA new tenant starts missing runs in the current incident cycle
API Reference
Base URL: https://api.syncguard.dev/api/v1. Authenticate with X-Api-Key on all requests.
| Method | Path | Description |
|---|---|---|
| GET | /jobs | List all monitored jobs with current status |
| POST | /jobs | Register a new job |
| GET | /jobs/{jobKey} | Job detail with per-tenant states and ping URL |
| PATCH | /jobs/{jobKey} | Update job config (interval, grace, thresholds, name) |
| DELETE | /jobs/{jobKey} | Soft-deactivate a job |
| POST | /jobs/{jobKey}/ping | Send a ping (success · failure · start) |
| GET | /incidents | List incidents (open, recovering, closed) |
| GET | /incidents/{id} | Full incident detail |
| GET | /incidents/{id}/tenants | Tenants with missedRuns, firstSeenAt, lastSeenAt |
| GET | /incidents/{id}/timeline | Status transitions ordered by time |
| POST | /incidents/{id}/resolve | Manually close an incident |
| GET | /tenants | List tenants |
| POST | /tenants | Create a tenant |
Response envelope
// Success { "isSuccess": true, "errorCode": 0, "message": "...", "data": { ... } } // Error { "isSuccess": false, "errorCode": 4, // 4 = ValidationError "message": "jobKey is required." }