Beta

API reference

REST events

POST /v1/events — report an agent's status from any tool or script.

POST /v1/events puts any agent on the board: another coding tool, a CI job, your own script. Send one event each time its state changes. Scope: ingest.

#The event

FieldTypeRequiredNotes
session_idstringYesYour ID for this run or session. One card per session_id (per tool). 1–128 of A–Z a–z 0–9 . _ : -
eventstringYesOne of the events below
toolstringNoYour tool's name, shown on the card. Default script. Up to 40 characters
projectstringNoThe project to file it under; created if new. Without it, the agent goes into Unsorted
reasonstringNoFor hold and error (and end with ok: false): a short reason shown on the card, up to 160 characters. Don't put secrets or content here
okbooleanNoFor end: false means the run failed
tsnumberNoWhen it happened, in milliseconds since 1970. Default: when it arrives. A time more than 3 days in the past or more than 1 day ahead is replaced with the time it arrives
eventThe agent becomesNotes
startStartingA new session. Reopens a closed one
activityWorkingAny sign of work. Send it at least every 30 minutes on long steps, or the session closes
holdNeeds youWaiting for a person. reason defaults to "Waiting for you"
turn_endIdleFinished a piece of work; the session stays open
errorFailedStopped by an error. reason defaults to "Error"
endDone (or Failed with "ok": false)The session is over

You don't have to send start first: any event creates the agent if it's new.

#Send one event

Start a session
curl -X POST "$AGENTOWER_URL/v1/events" \
  -H "authorization: Bearer $AGENTOWER_KEY" \
  -H "content-type: application/json" \
  -d '{"tool":"nightly-import","session_id":"run-2026-09-30","project":"data-pipeline","event":"start"}'
Response · 200
{ "ok": true, "accepted": 1 }
It needs a person
curl -X POST "$AGENTOWER_URL/v1/events" \
  -H "authorization: Bearer $AGENTOWER_KEY" \
  -H "content-type: application/json" \
  -d '{"tool":"nightly-import","session_id":"run-2026-09-30","project":"data-pipeline","event":"hold","reason":"Needs a person to check the report"}'
Response · 200
{ "ok": true, "accepted": 1 }

The card now shows Needs you, with "Needs a person to check the report" on its Now row, and any notification channels for Needs you are told.

#Send several at once

Send an array of up to 1,000 events. If any one is invalid, none are recorded.

A batch
curl -X POST "$AGENTOWER_URL/v1/events" \
  -H "authorization: Bearer $AGENTOWER_KEY" \
  -H "content-type: application/json" \
  -d '[{"tool":"nightly-import","session_id":"run-2026-09-30","event":"activity"},
       {"tool":"nightly-import","session_id":"run-2026-09-30","event":"end","ok":true}]'
Response · 200
{ "ok": true, "accepted": 2 }

#Errors

Missing event
curl -X POST "$AGENTOWER_URL/v1/events" \
  -H "authorization: Bearer $AGENTOWER_KEY" \
  -H "content-type: application/json" \
  -d '{"tool":"nightly-import","session_id":"run-2026-09-30"}'
Response · 400
{ "error": "invalid_event" }
Wrong key
curl -X POST "$AGENTOWER_URL/v1/events" \
  -H "authorization: Bearer at_test_00000000000000000000000000000000" \
  -H "content-type: application/json" \
  -d '{"session_id":"run-1","event":"start"}'
Response · 401
{ "error": "unauthorized", "hint": "Send an ingest key: authorization: Bearer at_…" }

Other errors are listed in Errors and limits.

#A small wrapper

From TypeScript or Python, use the SDKs: they send these reports for you, in order and without ever throwing into your agent, and can also wait for an answer from the app. From the shell, a function that reports the start, the end and any failure of a job:

report.sh
report() {  # report <event> [reason]
  curl -sS -X POST "$AGENTOWER_URL/v1/events" \
    -H "authorization: Bearer $AGENTOWER_KEY" -H "content-type: application/json" \
    -d "{\"tool\":\"nightly-import\",\"session_id\":\"$RUN_ID\",\"project\":\"data-pipeline\",\"event\":\"$1\",\"reason\":\"${2:-}\"}" >/dev/null
}
RUN_ID="run-$(date +%Y%m%d-%H%M)"
report start
if ./import.sh; then report end; else report error "import.sh failed"; fi