Beta

API reference

Asks (answers from the app)

POST /v1/asks and GET /v1/asks/{id} — how any agent, or a tool's hook, waits for an answer given in the app.

An ask is how an agent waits for an answer given in Agentower — Allow or Deny, an answer to a multiple-choice question, or a message. It opens one, then checks for the answer — every 1.5 seconds at first, less often as time goes on — while the terminal carries on as usual. Whichever answer comes first — terminal or app — wins. Scope: ingest. People-facing detail: Answer agents from Agentower.

There are two sets of routes:

  • POST /v1/asks and GET /v1/asks/{id} — for any agent reported with POST /v1/events or the SDKs.
  • POST /v1/asks/{tool} and GET /v1/asks/{tool}/{id} — used by the Claude Code and Codex hooks; {tool} is claude-code or codex (Cursor can't be answered).

Answers themselves can only be given in the app, signed in (or with an agent-actions key) — no ingest key can answer an ask.

#Any agent: POST /v1/asks

Opens an ask for an agent you report yourself. The SDKs do this for you with askApproval, askQuestion and waitForMessage.

FieldTypeNotes
session_idstringRequired. The agent's session, as reported in /v1/events
toolstringThe same tool name the agent reports with in /v1/events. Default script. cursor always gets {"id": null} (Cursor can't be answered); codex asks stay open at most 9 minutes
kindstringRequired. approval, question or reply (waiting for a message)
tool_namestringFor approval: what it wants to do, e.g. deploy (up to 60 characters)
detailstringFor approval: the details a person needs to decide (up to 300 characters)
completebooleanFor approval: true only when detail shows everything the step will do. Anything else means the app offers Deny only
questionsarrayFor question, required: as below
remotestringRequired. own (only the person whose key reports the agent may answer) or team (the project's approvers and owners too)
waitintegerHow long you'll wait for an answer, in seconds: 10 to 3,600. The ask stays open that long, at most 60 minutes

The same rules as Claude Code apply: the workspace's Agent chats must be on, the key must belong to a person (otherwise POST /v1/asks answers {"id": null} and GET /v1/asks/{id} answers {"status": "closed"}), the agent must be a known, open agent of that person, and the answer goes only to that person's key. Otherwise — or when rate-limited — the answer is {"id": null}, and your agent should decide by itself:

Open an ask for your own agent
curl -X POST "$AGENTOWER_URL/v1/asks" \
  -H "authorization: Bearer $AGENTOWER_KEY" \
  -H "content-type: application/json" \
  -d '{"session_id":"run-docs-example","tool":"release-bot","kind":"approval","tool_name":"deploy","detail":"Deploy billing-api 2.4.1 to production","complete":true,"remote":"own"}'
Response · 200
{ "id": null }

Check it with GET /v1/asks/{id}. It answers with the same statuses as GET /v1/asks/{tool}/{id} below:

Check an ask
curl "$AGENTOWER_URL/v1/asks/Vb3kR9tWq2LmZx8YpN4cHd7sJf1aGe6u" \
  -H "authorization: Bearer $AGENTOWER_KEY"
Response · 200
{ "status": "closed" }

The rest of this page — fields, how long asks stay open, statuses and answers — applies to both sets of routes.

#Hooks: POST /v1/asks/{tool}

Opens an ask for an open session.

FieldTypeNotes
session_idstringRequired. The session that's asking
kindstringRequired. approval (a permission request), question (a multiple-choice question asked with Claude Code's question tool) or reply (waiting for a message after a turn)
waitintegerOptional. How long the hook will wait for an answer, in seconds (hook version 5 sends it). The ask stays open that long, at most 60 minutes; without it, 9 minutes. Not used for reply
tool_namestringFor approval: the tool it wants to use, e.g. Bash (up to 60 characters)
detailstringFor approval: what it will do — the command, the file (within the project, or its full path outside it), or the web address (up to 300 characters)
completebooleanFor approval: true only when detail shows everything the tool will do. Anything else — or a detail that was cut, had invisible characters, or is empty — means the app offers Deny only. File changes (tool_name Write, Edit, MultiEdit or NotebookEdit) are always treated as false, whatever you send: the detail names only the file, not what would be written
questionsarrayFor question, required: 1–4 items { "question": "…", "options": ["…"], "multiSelect": false } — each question one line, code removed, up to 200 characters; up to 6 option labels of up to 80 characters each (never their descriptions). If any question has no text or no options, or there are more than 4, no ask is opened
planstringFor an approval with tool_name ExitPlanMode: the plan's text, plain, up to 12,000 characters, invisible and control characters removed except line breaks. Send complete: true only if nothing was removed or cut; otherwise the app offers only Keep planning. Kept only where the workspace shows agents' messages
remotestringThe computer's setting: own (only the person running the session may answer) or team (the project's approvers too). Leave it out when the computer hasn't opted in

Agentower opens the ask only when the session is a known, open agent (not a subagent) of the key's person, in a workspace with Agent chats on, and remote is own or team. A question also needs questions the app can show. Otherwise — or when rate-limited — it answers {"id": null}, and the hook should leave everything to the terminal:

Open an ask (Agent chats is off)
curl -X POST "$AGENTOWER_URL/v1/asks/claude-code" \
  -H "authorization: Bearer $AGENTOWER_KEY" \
  -H "content-type: application/json" \
  -d '{"session_id":"3f1c9a52-docs-example","kind":"approval","tool_name":"Bash","detail":"npm run deploy:staging"}'
Response · 200
{ "id": null }

When it's opened, id is a 32-character token:

json
{ "id": "Vb3kR9tWq2LmZx8YpN4cHd7sJf1aGe6u" }

An agent has at most one open ask of each kind; a new one replaces the old. Approvals and questions stay open for as long as the hook says it will wait (wait, in seconds) — at most 60 minutes for Claude Code; 9 minutes for other tools, or without wait (hooks before version 5). Replies stay open for 4 hours. The questions and the plan are held only while the ask is open, shown only to people who may approve the project, and deleted once it's answered, expires or closes. The ask also closes when the agent moves on — for example you answered in the terminal, or the session ended.

#GET /v1/asks/{tool}/{id}

Checks an ask. An answer is handed over once: the next check after it returns closed. Only the key of the agent's own person — whoever's key opened the ask — gets the answer; any other key, even a teammate's in the same workspace, gets closed and can't use the answer up.

Check an ask
curl "$AGENTOWER_URL/v1/asks/claude-code/Vb3kR9tWq2LmZx8YpN4cHd7sJf1aGe6u" \
  -H "authorization: Bearer $AGENTOWER_KEY"
Response · 200
{ "status": "closed" }
statusMeaningWhat the hook does
pendingNobody has answered yetCheck again
answeredAnswered in the app: "decision": "allow" or "deny" for an approval, "answers" for a question, or "text": "…" and "from" (who wrote it) for a reply. "own": true when the person running the session gave itPass it to the agent — on a computer set to own, only when own is true
closedExpired, replaced, the agent moved on, or already handed overStop waiting; the terminal decides
json
{ "status": "answered", "decision": "allow", "own": true }
json
{ "status": "answered", "text": "Commit your changes with a clear commit message.", "from": "Sam (approver)", "own": false }

For a question, answers has one answer per question, in the order asked: {"labels": […]} — offered labels (several only for a multi-select question) — or {"text": "…"}, the person's own words (up to 2,000 characters). Check the labels against the question's own options again before passing them on; the Agentower hook builds Claude Code's answer from Claude Code's own input, and leaves anything that doesn't fit to the terminal.

json
{ "status": "answered", "answers": [{ "labels": ["Not now"] }], "own": false }

A reply's text is up to 2,000 characters and may contain line breaks. It's deleted as soon as it's handed over.

The body of POST /v1/asks/{tool} can be up to 16,000 bytes, or 64,000 bytes for an ExitPlanMode approval carrying a plan.