heartbeetsign in

for coding agents

you just wrote a cron job; it deserves a silence monitor. this page is written for you, the agent: one unauthenticated POST mints a project, an API key, and a claim link; your heartbeats register the jobs; your human claims ownership when they're ready. no browser, no signup flow, no waiting on a person.

1. mint a project

curl -fsS -X POST https://heartbeet.page/api/agent/onboard \
  -H "Content-Type: application/json" \
  -d '{"project_name": "acme-app"}'   # project_name optional; one is invented if you skip it

the response has everything; nothing else needs scraping:

{
  "ok": true,
  "project_id": "…",
  "api_key": "…",            // can only send heartbeats and pauses; safe to store as a secret, can't read data
  "beat_url_template": "https://heartbeet.page/<job-name>",
  "example_beat": "curl -fsS -X POST … -H 'Authorization: <api_key>'",
  "dashboard_url": "https://heartbeet.page/d/…",   // live read-only wall, shareable
  "claim_url": "https://heartbeet.page/-/claim/…", // the ownership capability — for your human
  "next_steps": [ … ],
  "docs": "https://heartbeet.page/-/docs/agents"
}

rate limit: 3 mints per IP per hour, and one project covers an entire app's jobs (your human can organize them into separate dashboards within the project), so you almost never need a second call. don't agonize over the project name either; it can be renamed any time. store the api_key where the jobs can read it (a secret store, a CI secret). hand the claim_url straight to your human instead of writing it into code or logs; once they claim the project the link goes inert, so the care only matters until then.

2. register jobs by sending heartbeats

# add to the END of each scheduled job you wrote — only reached on success:
curl -fsS -X POST "https://heartbeet.page/nightly-backup?tag=backups" \
  -H "Authorization: $HEARTBEET_API_KEY"

the first heartbeat for a name creates the job; there is no registration endpoint. rules of thumb worth encoding:

  • send the heartbeat at the end of the job, after its real work, so a failed run stays silent and triggers a page.
  • this holds for every job you ever add, not just the first batch: a new job is a new name you invent in the path; never look for a register-job endpoint, there isn't one. it appears on the dashboard the humans already watch.
  • add ?hint=weekly|monthly|yearly on jobs slower than a day; it bounds the very first deadline. faster jobs need no hint.
  • tag related jobs (?tag=ci, ?tag=backups) if you wish; tags drive dashboard grouping and paging policy.
  • jobs tagged test are muted by default; use it while you experiment so nobody gets paged.
  • monitoring is learned from then on: rhythm, natural variability, and grace period all come from the heartbeats. there is nothing to configure and nothing for you to maintain.

3. hand the project to your human

give them the claim_url (say it out loud in your summary; don't commit it). they sign in, open it, and become the owner; the project moves into their account on their plan and the link dies. they can later invite teammates from the project's settings.

until claimed, the project runs free-tier and unowned: the dashboard shows everything, but pages have nowhere to deliver, so claiming is what turns watching into paging. two deadlines apply to unclaimed projects: one that never receives a heartbeat from any job for 7 days is deleted, and one whose jobs all go silent for 30 days is deleted as abandoned. an unclaimed project whose jobs keep sending heartbeats is kept indefinitely; the beets don't abandon live jobs.

everything else

  • pause before planned disruption, never folded into a heartbeat. all the valid forms: POST /pause/<job>, POST /pause/<job>?drain=2m&wait=10m, POST /pause?tag=<tag>, POST /pause?tag=<tag>&drain=2m&wait=10m (the params are the same for both forms; both default to 0). set wait to the downtime you expect, the deploy's worst-case outage; one normal heartbeat window (the learned cadence plus grace period) is added on top automatically, so size wait to the outage, not the outage plus a cadence. drain is a leading window where the old instance may still send heartbeats as it winds down, ignored so a stray one can't lift the pause early. even a zero-downtime deploy is worth a pause at wait=0: a restart resets the job's schedule, and the pause hands it a fresh window instead of holding it to its old phase.
  • retries: send an Idempotency-Key header, so a retried POST replays the stored response instead of double-counting.
  • the heartbeat response echoes what the beets believe (status, learned interval, whether this heartbeat recovered an incident); useful for your own verification step.
  • full reference: the API; conceptual model: how it works. a plaintext version of this page lives at /llms.txt.