> ## Documentation Index
> Fetch the complete documentation index at: https://docs.overwatchapp.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Cron job and scheduled task monitoring with Overwatch

> Set up heartbeat monitoring for cron jobs and scheduled tasks. Your job pings Overwatch at the end of each run — no inbound probing needed.

Scheduled monitor checks work the opposite way from HTTP or TCP monitors: instead of Overwatch probing your infrastructure, your cron job or scheduled task pings Overwatch to say it ran. If Overwatch doesn't hear from your job within the expected window, it marks the monitor as `havent_heard` and fires your configured alerts. This lets you detect silent failures — jobs that stop running without throwing any visible errors.

## How it works

<Steps>
  <Step title="Create a scheduled monitor">
    Send a `POST` request to `/api/v1/monitors` with `type: "SCHEDULED"`. The response includes a `checkinUrl` unique to that monitor.
  </Step>

  <Step title="Add the check-in URL to your job">
    At the end of your cron job or scheduled task, make an HTTP request to the `checkinUrl`. A successful request tells Overwatch the job ran.
  </Step>

  <Step title="Signal failures when needed">
    If your job detects an error, append `?status=fail` to the check-in URL. Overwatch records the run as failed and fires alerts immediately.
  </Step>
</Steps>

## Status values

| Status         | Meaning                                                                          |
| -------------- | -------------------------------------------------------------------------------- |
| `ok`           | The job checked in successfully within the expected window.                      |
| `failed`       | The job checked in with `?status=fail`, indicating the run encountered an error. |
| `havent_heard` | The expected check-in window has passed with no ping received.                   |

## Creating a scheduled monitor

```bash theme={null}
curl -X POST https://overwatchapp.dev/api/v1/monitors \
  -H "Authorization: Bearer ow_live_sk_<secret>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Nightly report job",
    "type": "SCHEDULED"
  }'
```

The response includes a `checkinUrl` field. Save this URL — you'll call it from your job.

```json theme={null}
{
  "data": {
    "id": "mon_01hx...",
    "name": "Nightly report job",
    "type": "SCHEDULED",
    "config": {
      "interval": 300
    },
    "status": "unknown",
    "enabled": true,
    "lastCheckIn": null,
    "lastCheckInStatus": null,
    "checkinUrl": "https://overwatchapp.dev/api/v1/monitors/mon_01hx.../checkin",
    "createdAt": "2026-04-15T10:00:00.000Z",
    "updatedAt": "2026-04-15T10:00:00.000Z"
  }
}
```

<Note>
  The `interval` for `SCHEDULED` monitors is always set to `300` seconds (5 minutes). This is the grace window: if Overwatch doesn't receive a check-in within 5 minutes of the expected time, it marks the monitor as `havent_heard`. You cannot configure a different interval for this monitor type.
</Note>

## Calling the check-in URL

The check-in endpoint accepts both `GET` and `POST` requests and requires no authentication. Call it at the end of your job to record a successful run.

```bash theme={null}
# Record a successful run
curl https://overwatchapp.dev/api/v1/monitors/<id>/checkin

# Record a failed run
curl "https://overwatchapp.dev/api/v1/monitors/<id>/checkin?status=fail"
```

The endpoint responds with a short confirmation:

```json theme={null}
{ "recorded": "ok", "monitor": "mon_01hx..." }
```

Or, for a failure check-in:

```json theme={null}
{ "recorded": "failure", "monitor": "mon_01hx..." }
```

## Code examples

<CodeGroup>
  ```bash bash / cron theme={null}
  #!/bin/bash
  # Run your job logic here
  python /app/scripts/nightly_report.py

  # Check in on success
  curl -s "https://overwatchapp.dev/api/v1/monitors/mon_01hx.../checkin" > /dev/null

  # Example crontab entry (runs at 02:00 every day):
  # 0 2 * * * /path/to/run_report.sh
  ```

  ```javascript Node.js theme={null}
  const CHECKIN_URL = 'https://overwatchapp.dev/api/v1/monitors/mon_01hx.../checkin';

  async function runJob() {
    try {
      await doYourWork();
      await fetch(CHECKIN_URL);
    } catch (err) {
      console.error('Job failed:', err);
      await fetch(`${CHECKIN_URL}?status=fail`);
      process.exit(1);
    }
  }

  runJob();
  ```

  ```python Python theme={null}
  import requests

  CHECKIN_URL = "https://overwatchapp.dev/api/v1/monitors/mon_01hx.../checkin"

  try:
      do_your_work()
      requests.get(CHECKIN_URL, timeout=5)
  except Exception as e:
      print(f"Job failed: {e}")
      requests.get(f"{CHECKIN_URL}?status=fail", timeout=5)
      raise
  ```
</CodeGroup>

<Tip>
  Always call the check-in URL as the very last step in your job, after all work has completed. If your job script exits early due to an error, use a `trap` (bash) or `try/except` (Python) to fire a `?status=fail` check-in so Overwatch knows the run failed rather than going silent.
</Tip>

<Warning>
  The check-in endpoint only accepts requests for monitors that are `enabled`. If you pause a scheduled monitor, check-in requests return `404` until you re-enable it.
</Warning>
