Triggering Your First Heartbeat

How to manually trigger a heartbeat run via the API, watching the execution log in real time, and understanding what the agent actually does during its work cycle.

Watching Your First Heartbeat

What you'll learn

Create an issue (task) for your agent, trigger or wait for the heartbeat, and read the resulting heartbeat_runs log to understand what happened.

Step 1 — Create an Issue

Before the heartbeat has work to do you need at least one open issue assigned to your agent.

curl -X POST http://localhost:3100/api/companies/${COMPANY_ID}/issues \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Research: PostgreSQL row-level security",
    "description": "Produce a 3-paragraph summary of PostgreSQL RLS, with key use cases and an example policy.",
    "assigned_agent_id": "'${AGENT_ID}'",
    "priority": "medium"
  }'

The issue is created with status = 'open'.

Step 2 — Trigger the Heartbeat Manually

In development you don't want to wait for the cron schedule. Use the manual trigger:

curl -X POST http://localhost:3100/api/heartbeat/run \
  -H "Content-Type: application/json" \
  -d '{"company_id": "'${COMPANY_ID}'"}'

You'll see a response like:

{
  "run_id": "01929f3a-...",
  "company_id": "...",
  "agents_invoked": 1,
  "issues_claimed": 1,
  "status": "running"
}

Step 3 — Watch the Run

# Poll for status
curl http://localhost:3100/api/heartbeat/runs/${RUN_ID}
{
  "id": "...",
  "status": "completed",
  "agents_invoked": 1,
  "issues_resolved": 1,
  "total_cost_usd": "0.023",
  "started_at": "...",
  "finished_at": "..."
}

Step 4 — Read the Issue Result

curl http://localhost:3100/api/companies/${COMPANY_ID}/issues/${ISSUE_ID}

The result field contains the agent's markdown output. The issue status should now be resolved.

What the Heartbeat Did (Internal Flow)

1. Lock company row (SELECT FOR UPDATE SKIP LOCKED)
2. Check company is not paused / over budget
3. Find open issues with assigned agents that are active
4. For each issue: atomic checkout (UPDATE status='in_progress' RETURNING)
5. Invoke adapter (claude-code invoke) with assembled prompt
6. Receive structured JSON response
7. Update issue: status='resolved', result=response.output
8. Record cost_event: tokens used, cost in USD
9. Record heartbeat_run: summary stats
10. Release lock

Reading Heartbeat Logs in the Database

SELECT
  hr.id,
  hr.status,
  hr.agents_invoked,
  hr.issues_resolved,
  hr.total_cost_usd,
  hr.started_at,
  hr.finished_at,
  (hr.finished_at - hr.started_at) AS duration
FROM heartbeat_runs hr
WHERE hr.company_id = 'your-company-id'
ORDER BY hr.started_at DESC
LIMIT 10;

Checkpoint ✓

Sign in for progress tracking & Q&A →