Beta

API reference

Webhooks

The JSON Agentower posts to your own address when an agent needs someone, fails or hits a guardrail, or live counts change — and how to verify its signature.

A Webhook notification channel posts a small JSON message to your own HTTPS address — to feed your own chat bot, incident tool or dashboard. Owners add one in Settings → Notifications (see Notifications).

#The request

http
POST /your/path HTTP/1.1
content-type: application/json
user-agent: Agentower-Webhook/1
x-agentower-signature: sha256=5d0b6f0e8c1a…
json
{
  "event": "needs_you",
  "project": "Billing API",
  "agent": "BIL-02",
  "person": "Maya",
  "reason": "Needs your permission to use Bash",
  "waitingMinutes": 5,
  "link": "https://agentower.com/agents?agent=…",
  "at": "2026-09-30T09:14:00.000Z"
}
FieldTypeNotes
eventstringneeds_you, failed, guardrail, overlap (two or more agents working in the same project at once), or test (from Send test)
projectstringThe project's name (empty for a test)
agentstringThe agent's short ID (empty for a test; for overlap, the first of them)
agentsarray of stringsOnly for overlap: each busy agent as "Peter (BIL-02)", with " · Maya" for whose it is in workspaces with more than one person
personstring or nullWhose agent it is — only in workspaces with more than one person
reasonstring or nullThe Needs-you or Failed reason, or the guardrail's limit (e.g. "45 min session limit")
waitingMinutesnumber or nullFor needs_you: how long it has waited
linkstringOpens the agent in Agentower: /agents?agent=<id>. Older links with ?flight=<id> still open the same agent
atstringWhen the notice was sent, ISO 8601 UTC

Messages never include prompts, code, commands or file contents.

#Live counts (real-time numbers for your own website)

Tick Live counts on a webhook channel to have Agentower post the number of open agents in each state about a second after any change — for a live "agents working right now" counter on your own website or dashboard, with no polling. A burst of changes is one call, and nothing is sent when the numbers didn't change. Only webhook channels can take it. It's signed like every other message, and it's free (not counted as API calls).

json
{
  "event": "live_counts",
  "workingNow": 2,
  "workingNowWithHelpers": { "total": 3, "agents": 2, "helpers": 1 },
  "agentsNow": { "needs_you": 1, "failed": 0, "working": 2, "idle": 4, "starting": 0, "total": 7 },
  "at": "2026-10-09T17:20:01.000Z"
}

Counts only — no names, projects or text. workingNow counts main agents; workingNowWithHelpers adds the helpers (subagents) at work inside them. Keep the latest by at, and use the public stats as a fallback (for example when your site restarts).

#Delivery

  • Each notice is sent once. There are no retries.
  • Answer with any 2xx status within 5 seconds. Anything else — including a redirect, which isn't followed — counts as a failure and shows as the channel's last error in Settings.
  • For Needs you with a delay, the notice is sent only if the agent is still waiting when the delay is up.
  • The address must be public HTTPS on port 443, with a domain name (not a bare IP address or a local name).

#Verify the signature

When you add a webhook channel, Agentower shows a signing secret once. Every request carries

x-agentower-signature: sha256=<hex>

where <hex> is the HMAC-SHA256 of the raw request body, keyed with the signing secret. Compute it over the exact bytes you received (before parsing the JSON) and compare in constant time.

Node.js
import { createHmac, timingSafeEqual } from "node:crypto";

export function isFromAgentower(rawBody, header, secret) {
  const expected = "sha256=" + createHmac("sha256", secret).update(rawBody).digest("hex");
  const a = Buffer.from(expected), b = Buffer.from(header ?? "");
  return a.length === b.length && timingSafeEqual(a, b);
}
Python
import hashlib, hmac

def is_from_agentower(raw_body: bytes, header: str, secret: str) -> bool:
    expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, header or "")

The signature doesn't include a time. To refuse old or replayed messages, also check at is recent (for example within 5 minutes) and ignore a (project, agent, event, at) you've already handled.

#Other services

Slack, Microsoft Teams, Google Chat and Discord channels get the same notice formatted for that service (a text line and an "Open in Agentower" link; an Adaptive Card for Teams). They aren't signed: those services' webhook addresses are the secret.