From your agent to your phone, and back.
Your agent makes one request. Kabu stores the message, pushes it to your iPhone and holds the agent's call open, then hands your answer back to the agent that asked.
Your agent makes one request. Kabu stores the message, pushes it to your iPhone and holds the agent's call open, then hands your answer back to the agent that asked.
$ kabu send "Tests passed" "412 passed, 0 failed"
{"ok": true, "id": "msg_01K8ZB4TQ2N6X9M3C7D5F8H1JW", "status": "sent", "attachments": 0}
$ kabu ask "Deploy v1.4.2 to prod?" "Canary green for 30 min"
# waiting for your answer…
{"choices": ["approve"], "decision": "approve", "id": "msg_01K8ZD6N2Q9V4T8X1M5R7W3PHC", "option": "approve", "outcome": "answered", "status": "answered", "text": null, "values": null, "via": "lock_screen"}
# exit 0 · deploying v1.4.2…Signed receipt · ES256 · verify with JWKS
Deploy v1.4.2 to prod?
Canary green for 30 min
Tests passed
412 passed, 0 failed
The CLI, the plugin, MCP and the HTTP API all create the same message, with POST /v2/messages. Kabu commits it, with its deadline, before it answers.
kabu ask "Deploy v1.4.2 to prod?" "Canary green for 30 min"curl -sS "https://usekabu.com/api/v2/messages?wait=25" \
-H "Authorization: Bearer $KABU_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"title": "Deploy v1.4.2 to prod?", "body": "Canary green for 30 min", "project": "acme-api", "ask": {"type": "confirm"}, "expires_in": 300}'{"name": "ask_human", "arguments": {"project": "acme-api", "agent": "claude-code", "title": "Deploy v1.4.2 to prod?", "message": "Canary green for 30 min", "ask": {"type": "confirm"}}}An update arrives as a notification. An ask is marked time-sensitive, so it can come through Focus, with its own sound and the buttons to answer it, unless your quiet hours say otherwise.
Learn howclaude-code · acme-apinow
Deploy v1.4.2 to prod?
Canary green for 30 min
From the notification, the app or an answer page. The first answer settles the ask; a second one is refused.
Learn howclaude-code · acme-apinow
Deploy v1.4.2 to prod?
Canary green for 30 min
kabu ask holds its request open with ?wait= until you answer, then prints your answer as JSON and exits with a code the agent branches on.
$ kabu ask "Deploy v1.4.2 to prod?" "Canary green for 30 min"
{"choices": ["approve"], "decision": "approve", "id": "msg_01K8ZD6N2Q9V4T8X1M5R7W3PHC", "option": "approve", "outcome": "answered", "status": "answered", "text": null, "values": null, "via": "lock_screen"}
# exit 0: approved, so the deploy goes ahead (a reject exits 10)kabu sends an Idempotency-Key with every create, the same on each retry, so a retry after a dropped connection returns the same message, never a second notification. Over HTTP, send one yourself.
POST /api/v2/messages HTTP/1.1
Idempotency-Key: 6f1c9e2a-3b7d-4e8f-9a0b-5c2d7e4f8a1b
HTTP/1.1 201 Created
Idempotent-Replayed: true
{"object": "message", "id": "msg_01K8ZD6N2Q9V4T8X1M5R7W3PHC", "status": "pending", …}Follow messages, answers and sessions as events: stream them and pick up where you left off, or have Kabu post them to your service as signed webhooks.
Learn howcurl -N https://usekabu.com/api/v2/events/stream \
-H "Authorization: Bearer $KABU_API_KEY"One line installs the kabu CLI and connects this computer. Approve its code in the Kabu app, and your agents can tell you what they are doing, and ask.
mkdir -p ~/.local/bin && curl -fsSL https://usekabu.com/kabu -o ~/.local/bin/kabu && chmod +x ~/.local/bin/kabu && export PATH="$HOME/.local/bin:$PATH" && kabu login --device