How to get Push notifications when Cursor is done working

11.02.2026. at 08:23:18

I often leave Cursor to do some work and go off to play with my kids or something. But then I often miss the moment when it's done working, and it just hangs there waiting for me, wasting time.

The "done" chime sometimes doesn't work, and definitely doesn't work when there's a network interruption or an error, so I needed a better method.

Here's how to make Cursor send you a push notification when it's done working.

IMG_5850

Step 1: NTFY

Register for an NTFY.sh account (optional, actually, but lets you subscribe on web too so you get desktop notifications as well).

Install NTFY onto your phone and do nothing for now.

Step 2: Set up the Cursor hooks

Create ~/.config/cursor-notify/config.json:

{
  "server": "https://ntfy.sh",
  "topic": "cursor_done_SOME_RANDOM_STRING"
}

Replace SOME_RANDOM_STRING with an actual, unguessable random string. This is your unique hook identifier. If anyone gets it, they can just spam your hook and blow your plan.

Then, in the NTFY app, go to Subscribe, and input this topic.

Then make a hook in ~/.cursor/hooks/notify-ntfy.py.

#!/usr/bin/env python3
import json
import os
import subprocess
from pathlib import Path
from datetime import datetime

CFG_PATH = Path.home() / ".config" / "cursor-notify" / "config.json"

def main() -> None:
    # Read hook payload from Cursor
    try:
        payload = json.load(os.sys.stdin)
    except Exception:
        payload = {}

    status = payload.get("status", "unknown")
    conversation_id = payload.get("conversation_id", "")
    loop_count = payload.get("loop_count", None)

    # Load ntfy config
    cfg = json.loads(CFG_PATH.read_text())
    server = cfg.get("server", "https://ntfy.sh").rstrip("/")
    topic = cfg["topic"]

    repo = os.path.basename(os.getcwd())
    now = datetime.now().strftime("%H:%M:%S")

    # Optional: reduce noise (only notify on "completed")
    # Cursor docs/examples commonly show status as completed/aborted/error. :contentReference[oaicite:6]{index=6}
    if status != "completed":
        # Still must output JSON to Cursor
        print("{}")
        return

    msg = f"Cursor finished in {repo} at {now} (status: {status})"
    if loop_count is not None:
        msg += f" | loop={loop_count}"
    if conversation_id:
        msg += f" | convo={conversation_id}"

    # Send push
    # ntfy supports headers like Title/Priority/Tags and Click actions. :contentReference[oaicite:7]{index=7}
    url = f"{server}/{topic}"
    subprocess.run(
        [
            "curl", "-sS", "--max-time", "2", "--retry", "1",
            "-H", "Title: Cursor Agent",
            "-H", "Priority: high",
            "-H", "Tags: robot_face,white_check_mark",
            "-d", msg,
            url,
        ],
        check=False,
        stdout=subprocess.DEVNULL,
        stderr=subprocess.DEVNULL,
    )

    # Hooks talk JSON back to Cursor
    print("{}")

if __name__ == "__main__":
    main()

Make it executable with chmod +x ~/.cursor/hooks/notify-ntfy.py.

Restart Cursor.

Now you will be notified whenever it's done working, and you can also find all past notifications in the app, retained for ~12 hours.

IMG_5851