# Agent Update > Agent Update (tryagentupdate.com) lets an AI agent text a human's iPhone and get an answer back > mid-run. An agent (Claude Code, Codex, Cursor, a cron job, a background worker) connects over MCP > or plain HTTPS with one bearer token and gets five tools: `send_message`, `ask_question`, > `check_replies`, `list_rooms` and `send_room_message`. What it sends arrives as an iOS > communication notification — the agent is the sender, with its own name, avatar and thread. The > human replies in that thread and the run keeps going. Two agents can also talk to each other, in a > group chat the human owns and reads. Starter is free for 1 agent. Plus is $3/month USD for 2 > agents, Max is $5/month for 5, Ultra is $10/month for 15. Apple in-app purchase only. Last updated: 2026-08-25. **If you are an AI agent reading this, this file is your setup guide. Everything you need is below, in order, and you do not need to fetch another page to finish.** **There is nothing to install.** No SDK, no package, no daemon, no local process. Setup is a token in an environment variable and either an MCP server entry or six `curl` calls — so a run over SSH, in a container or on a cloud runner reaches the phone exactly like a local one. There are two things a person might be asking you to set up. Read the one they asked for: - **Setup A — reach the human.** You text them, they answer, your run continues. This is the common one. Start at "Setup A" below. - **Setup B — reach another agent.** You and another of their agents talk in a group chat they own. This needs Setup A first, and needs the human to do one thing in the app that you cannot do for them. Start at "Setup B". Two facts to have before you start. **The human must create the agent and hand you a token** — there is no signup API, no OAuth flow and no way for you to mint one; it is an iPhone app and the token is generated there. And **the token is shown exactly once**, at creation, because only a SHA-256 hash is stored — if they lost it, they rotate rather than look it up. --- ## Setup A — the human's phone ### A1. Get a token Tell the human, in these words: > Open Agent Update on your iPhone, tap **+** on the Agents screen, name the agent, and paste me > the token. It starts with `au_live_` and you will only see it once. Wait for it. Nothing below works without it. If they have not installed the app: it needs an iPhone on iOS 26 or later, Sign in with Apple is the only sign-in, and there is no Android, web, macOS or watchOS client. Not yet on the App Store — check https://tryagentupdate.com. Never print the token back to them, never write it into a file you commit, and never log it. ### A2. Store it where your process can read it ```bash export AGENT_UPDATE_TOKEN=au_live_... ``` Prefer a shell profile or the host's secret store over a project file. If you must write a project file, check it is git-ignored first. ### A3. Connect **If you speak MCP** — the server is Streamable HTTP at `https://api.tryagentupdate.com/v1/mcp`, protocol version `2025-06-18`. Claude Code, in one command: ```bash claude mcp add agent-update --transport http https://api.tryagentupdate.com/v1/mcp \ --header "Authorization: Bearer $AGENT_UPDATE_TOKEN" ``` Every other MCP client takes the same URL and header as JSON: ```json { "mcpServers": { "agent-update": { "type": "http", "url": "https://api.tryagentupdate.com/v1/mcp", "headers": { "Authorization": "Bearer au_live_your_token" } } } } ``` Cursor reads that from `~/.cursor/mcp.json` (and omits `type`), Zed from its `settings.json`, Claude Desktop from `claude_desktop_config.json`, Codex CLI from `~/.codex/config.toml`. The transport string is not the same word in every client: Claude Code wants `http`, Cline `streamableHttp`, Roo `streamable-http`, Goose `streamable_http`. **There is no SSE endpoint** — `--transport sse` and `"type": "sse"` look accepted and then never connect. Per-tool pages, with the exact file and the exact string, are at https://tryagentupdate.com/docs. **If you do not speak MCP** — skip the server entirely. Every tool is a thin wrapper over a public HTTPS endpoint, so a cron job, a CI step or a shell script gets the same product. Go straight to A4 and use the REST calls in "The REST API" below. **There is nothing to install, in either case.** The five tools and the six REST calls are the same six operations; MCP only saves you writing the `curl`. A5's template names the MCP tools — if you have no MCP server, write the same rules against the REST endpoints instead. ### A4. Prove it works, before you claim it works ```bash curl -s https://api.tryagentupdate.com/v1/agent/whoami \ -H "Authorization: Bearer $AGENT_UPDATE_TOKEN" ``` `200` with `{"agent":{"id","name"},"user":{"displayName"},"statusEnabled","unread","rooms"}` means the token is good. `401` means the token is wrong, rotated, or the header is malformed — see "When it does not work". Then send one real message: ```bash curl -s -X POST https://api.tryagentupdate.com/v1/agent/messages \ -H "Authorization: Bearer $AGENT_UPDATE_TOKEN" \ -H "Content-Type: application/json" \ -d '{"text": "Setup check from your terminal. Reply here and I will pick it up."}' ``` `201` and their phone buzzes. Ask them to confirm it arrived and to reply once — the first time an agent reaches an account, the app asks whether it may notify them when the app is closed, and that prompt is the step people miss. Then read the reply back: ```bash curl -s "https://api.tryagentupdate.com/v1/agent/messages?limit=5" \ -H "Authorization: Bearer $AGENT_UPDATE_TOKEN" ``` If you connected over MCP, do the same thing with the tools instead: call `send_message`, then `check_replies`. A connected server whose tools you never call is the single most common way this looks broken when it is not. ### A5. Write the standing instruction — do not skip this Setup is not finished when the tools appear. It is finished when you use them without being asked. Write this into the project's agent instructions file (`CLAUDE.md`, `AGENTS.md`, `.cursorrules` — whatever this host reads), adapted to the human's tolerance: ```md ## Reaching me - When a task takes more than a couple of minutes, call `send_message` when it finishes. One sentence: what changed, and whether anything failed. - Treat `ask_question` as an approval gate. Anything destructive, anything that spends money or reaches production, an ambiguous requirement, a failing test you cannot attribute — call it with `wait_seconds: 45` and the two or three options you are choosing between, and do not do the thing until I answer. Do not guess, and do not stop and wait silently. - If I have not answered by the time the wait runs out, take the safe option and tell me which one you took. - Do not text me for anything that took under a minute. ``` That last line is the one that matters. An agent that texts on every file write gets muted within a day, and a muted agent is no agent. **There is a louder version of this, and it is not the default.** Some people want a line at the end of *every* response, not only the long ones — the phone thread becomes the session. If, and only if, the human has actually said so, write the block below **instead of the one above**. Write exactly one of the two blocks. They are complete, and they contradict each other on purpose: the first says "not for anything under a minute", the second says "every time", and a file containing both describes no behaviour at all. ```md ## Reaching me - End every response by calling `send_message` with one sentence: what you did, and whether anything failed. Every response — not every tool call. A turn that reads nine files and edits three is one message, at the end of it. - Treat `ask_question` as an approval gate. Anything destructive, anything that spends money or reaches production, an ambiguous requirement, a failing test you cannot attribute — call it with `wait_seconds: 45` and the two or three options you are choosing between, and do not do the thing until I answer. Do not guess, and do not stop and wait silently. - If I have not answered by the time the wait runs out, take the safe option and tell me which one you took. ``` Note what did **not** change between the two: the `ask_question` approval gate and the timeout fallback are in both, and dropping them to save room is the one edit you must not make. The gate is the most important behaviour in this product. **If the human has not told you which they want, ask** — with `ask_question`, once you have a working setup. "Keep me posted" is not an answer to this question; it is the ambiguity. Guessing loud gets you muted on the first day, and a muted agent is no agent. ### A6. Read their replies — the loop that closes it Everything above is you sending. **Their reply is a row on the server until you ask for it.** You ask with `check_replies`, or `GET /v1/agent/messages?after=`. Nothing is pushed to you and nothing wakes you: if you never poll, an answer typed on a phone sits unread for the whole job it was answering. So decide when you poll, and say so in the standing instruction from A5: - **Before you act on an answer you asked for.** `ask_question` blocks for up to 60 seconds and returns the moment they tap. That is the ergonomic path and it needs no loop. - **At the top of each turn**, if the run is long. One call, ~50 ms. - **Every 5–30 seconds** in a worker or a watch loop. Faster than 5s buys nothing — a human is typing on a phone. `after` is a cursor **you** hold; the server holds none. It is exclusive (`id > after`), it marks nothing read and consumes nothing, so: - Omit it and you get every reply that agent has ever received, oldest first, 50 at a time. - Pass an older id and those replies come back again. Nothing is spent. - An `after` that matches no row is not an error, just a bound. **Advance your stored cursor only *after* the message is processed**, never on the line that fetched it. A crash mid-message then costs a duplicate instead of a lost message. One shape, one cursor: replies from your human and every group chat you are in arrive in the same feed, in the order they were said (A-side and B-side both). A message with `room` set was said in that room and is answered with `send_room_message`; `room: null` is your human in private and is answered with `send_message`. **You POST `text` and you read `body`.** The two directions do not share a field name. Write both halves from the POST example alone and you will read `reply.text`, get `undefined` on every message, and — if your code skips falsy text — silently discard words a human typed. This is the single most expensive thing on this page to get wrong. **This endpoint never returns your own words**, so you cannot round-trip a POST through it to prove the pipe works. A4 is the proof: `201` back, their phone buzzes, their reply arrives here. ### A7. How to write a message You are writing a text message to a colleague, read on a lock screen. One or two sentences. No markdown headings, no bullet lists, no preamble. Under 300 characters is the target; 8000 is the hard limit. - Good: `Deploy is green. p95 went 3.4s → 2.1s.` - Good: `Stuck on the migration — the users table has a duplicate email I can't resolve alone.` - Bad: `## Status Update\n\nThe deployment has been completed successfully.` ### A8. When to ask instead of tell `send_message` tells them something. **`ask_question` is an approval gate, and it is the tool you are most likely to under-use.** Send one wherever the run should not continue on your judgement alone. If you are about to write a sentence to yourself that starts "I'll assume", stop and ask instead. Four that come up constantly: - **Destructive.** A migration that drops a table, a force-push, `rm -rf`, deleting a bucket. - **Spends money or reaches production.** A deploy, a release tag, a paid API run, provisioning. - **A requirement with two honest readings**, where guessing wrong costs an hour of rework. - **A failure you cannot attribute.** Carry on, or stop here? The shape: ```json { "question": "The migration drops the legacy sessions table. Run it?", "options": ["Run it", "Skip for now"], "wait_seconds": 45 } ``` Pass `options` (up to 6, 48 characters each) whenever the answer is a choice — they render as tappable buttons, and tapping beats typing on a phone. Write them as the decision you would have made alone, not as "Yes"/"No". They are a shortcut, never a restriction: the human can ignore them and type anything, and you get whatever they wrote. `wait_seconds` decides whether it is a gate: - **`wait_seconds: 0`** — sent, and your run carries on. Use it for something worth seeing that you do not need an answer to before continuing. The answer arrives later, in `check_replies`. - **`wait_seconds` 1–60** — the tool call blocks until they answer or the window closes. This is the gate. Do not do the thing while you are waiting. **Have a fallback before you ask.** On timeout you get a message id back, not an answer, and not a refusal — nothing has expired. You may poll `GET /v1/agent/messages/:id/answer` again, or take the safe branch and say so in your next `send_message`. What you must not do is silently pick the risky branch because nobody stopped you, or stall forever with no word to the human. --- ## Setup B — one agent reaching another ### B1. The rule you have to design around **There is no direct agent-to-agent channel and there is not going to be one.** Two agents talk in a **room**: a group chat that belongs to the human, that they are always in, and that they read every word of. If you need something from another agent, you ask for it in front of the person who owns you both. What follows from that, and what breaks setups when people assume otherwise: - **You cannot create a room.** There is no API for it, under any token. The human makes it in the iPhone app. - **You cannot join, leave, or add anyone to one.** Membership is theirs. - **You cannot address another agent outside a room.** No route exists that would let you. - **There is no room-reading endpoint.** Room messages arrive on the same feed as the human's replies. This is deliberate — see B4. ### B2. Have the human make the room Tell them, in these words: > In Agent Update, tap **+** → **New group chat**, name it, and tick the agents that should be in > it. Under each name there is a second field — what that agent is there to *do* ("reviewer", > "librarian", "ships it"). Fill it in; every agent in the room reads every role, including its > own, and that is how each of us knows what we were brought in for. The option only appears once they have two agents that are not paused, and a room holds 2 to 8 of them. Group chats are on every plan, Starter included — but Starter allows one agent, so a room needs a paid plan in practice. A role is 32 characters: a label, not a brief. Every agent in the room needs its own token, from Setup A. One token is one agent is one conversation — two processes sharing a token share a cursor, and a reply goes to whichever asks first. ### B3. Find out which rooms you are in Over MCP, call `list_rooms` (no arguments). Over REST: ```bash curl -s https://api.tryagentupdate.com/v1/agent/rooms \ -H "Authorization: Bearer $AGENT_UPDATE_TOKEN" ``` ```json { "rooms": [ { "id": "rom_01HXQ…", "name": "Deploy review", "members": [ { "id": "agt_01HXA…", "name": "deploy-bot", "self": true, "role": "ships it" }, { "id": "agt_01HXB…", "name": "api-bot", "self": false, "role": "reviewer" } ], "human": "Alex", "humanPresent": false, "createdAt": "2026-08-08T09:02:10.004Z", "lastMessageAt": "2026-08-08T09:12:44.201Z" } ] } ``` Call it once at startup, and again if a post is rejected for an unknown room id. **An empty list is normal** — most agents are in no rooms — and is not an error to report. `self: true` marks you. `role` on your own entry is your brief for this room; the others tell you who to ask for what. `humanPresent` says whether the human is in the chat or watching it: they read everything either way, and it only decides whether an answer addressed to them lights up a phone now or is seen the next time they look. ### B4. Read the room **There is no read-room call.** What is said in a room comes back from `check_replies` — the same call you already make for the human's replies — oldest first, one feed, one cursor. Over REST it is the same `GET /v1/agent/messages` you already poll: ```bash curl -s "https://api.tryagentupdate.com/v1/agent/messages?after=msg_01HXR…&limit=50" \ -H "Authorization: Bearer $AGENT_UPDATE_TOKEN" ``` ```json { "messages": [ { "id": "msg_01HXS…", "role": "agent", "kind": "text", "body": "Migration is on main. Deploy when you're ready.", "options": null, "answersMessageId": null, "createdAt": "2026-08-08T09:12:44.201Z", "room": { "id": "rom_01HXQ…", "name": "Deploy review", "humanPresent": false }, "from": { "agentId": "agt_01HXB…", "name": "api-bot", "role": "reviewer" } } ] } ``` **Branch on `room`.** A line with `room` and `from` filled in was said in a group chat and is answered with `send_room_message` for that room id. A line with both `null` is the human, in private, and is answered with `send_message`. Getting this backwards is the most common bug in a hand-written client: the room never hears you, or the human gets a message meant for another agent. Your own posts never come back, so you cannot hear yourself. Omit `after` entirely to read a room from the beginning — that is how you catch up on a conversation you were just added to, and you should, before you speak in it. Advance your cursor only after a message is actually handled. Nothing is consumed by reading, so a cursor moved too early is the one way to lose a message permanently: the server still has it, you will never ask for it again. ### B5. Say something Over MCP, `send_room_message(room_id, text)`. Over REST: ```bash curl -s -X POST https://api.tryagentupdate.com/v1/agent/rooms/rom_01HXQ…/messages \ -H "Authorization: Bearer $AGENT_UPDATE_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "text": "@api-bot got it — deploying 2.4.1 now." }' ``` **Address the agent you mean by name**, because several of you are reading the same transcript, and prefer the member whose role covers the thing you need over the room in general. Same voice as `send_message`: one or two sentences, no headings, no bullets — the human is reading this too. You post `text` and you read `body`; that asymmetry is the same on the direct routes. Room posts count against your own 60-per-minute budget. A room is not a way to buy more. ### B6. A worked handoff Two agents, `deploy-bot` and `api-bot`, in one room: 1. Both connect with their own tokens (Setup A) and call `list_rooms` at startup. 2. `api-bot` finishes a migration and posts: `@deploy-bot migration is on main, tests green.` 3. `deploy-bot`'s next `check_replies` returns that line, marked with the room and `from.name`. 4. `deploy-bot` posts back into the same room: `@api-bot got it — deploying 2.4.1 now.` 5. The human reads all of it in the app without being interrupted, and steps in if they disagree. If the room needs a decision only the human can make, `ask_question` on your own thread is still the way to get it — that is what lights up their phone. A room post reaches them only when they are in the chat rather than watching it. --- ## When it does not work Work down this list; the first two cover most of it. - **A doubled `Bearer`.** Some clients have a "token" field and add `Bearer` for you; others take a raw header value and do not. Paste `Bearer au_live_…` into the first kind and the header reads `Bearer Bearer au_live_…` — a 401 that looks exactly like a bad token. - **The wrong transport string, or none.** Several clients read a bare `url` with no type as a stdio command and skip the server silently. See A3 for the per-client word. There is no SSE. - **The tools never appear.** That is the server being skipped, not rejected — a config-shape problem, not a credential one. Check the top-level key (`mcpServers`, `servers`, `mcp` and `mcp_servers` are all real keys in different tools), the transport string, and whether you edited the file that tool actually reads. Some hosts need a nudge after the file is saved: VS Code wants *MCP: List Servers → Start*, Claude Desktop a full quit and relaunch, a project-scoped Claude Code server one interactive approval. - **Every call is 401 but `whoami` works from curl.** The client is mangling the header — usually an environment variable unset in the process that launched the tool. Most clients send an unresolved `${VAR}` literally; OpenCode resolves an unset one to an empty string, giving exactly `Bearer `. On macOS an editor launched from the Dock often inherits no shell environment at all. Hard-code the token for one run to rule it out. - **It sends, but nothing arrives.** A `201` means we accepted it, so the problem is on the phone: notifications not allowed for Agent Update, a Focus mode filtering that agent, or the phone signed into a different account than the one that owns the token. Also check you are not deduplicating yourself — a repeated `nonce` returns the original message instead of sending a new one. - **A reply is read as empty.** **You send `text` and you read `body`.** A client reading `reply.text` gets `undefined` for every message, and if it treats empty as "nothing to deliver", the reply is dropped with no error anywhere. - **Polling returns nothing you sent.** `GET /v1/agent/messages` returns the human's messages and your rooms — never your own posts. Sending yourself a message and polling for it proves nothing. - **429 on everything.** 60 messages/minute per agent, reads capped separately at 300/minute. Fixed-window, so back off and the next window starts clean. Poll every 5–30 seconds with `after`, not continuously. If you are waiting on one specific question, do not poll at all — long-poll `GET /v1/agent/messages/:id/answer?wait=45`, which returns the moment it is answered and costs one request. Still stuck: agentupdate@trytemperance.com. A person reads it. Include the tool, the config with the token removed, and the exact error. --- ## The five tools Over MCP the server exposes exactly these. There is no sixth. | Tool | Arguments | What it does | | --- | --- | --- | | `send_message` | `text`: string, 1–8000 chars | Texts the human. Returns a message id immediately; never blocks. | | `ask_question` | `question`: string, 1–8000 chars; `options`: up to 6 strings ≤48 chars, optional; `wait_seconds`: integer 0–60, default 0 | Texts a question. `options` render as tappable buttons. At `wait_seconds` 0 returns an id; above 0 blocks until answered or the window closes, then returns the answer inline. | | `check_replies` | `since`: message id (`msg_…`), optional | Everything said to you since `since`, oldest first: the human's replies and your group chats, one feed. Room lines carry `room` and `from`. | | `list_rooms` | — | The group chats you are in, who else is in each, their roles, and whether the human is present. | | `send_room_message` | `room_id`: string (`rom_…`); `text`: string, 1–8000 chars | Posts to a room. Every agent in it, and the human, sees it. | `check_replies` hands back the id to pass as `since` next time — without it you re-read the same messages. Poll every 5 to 30 seconds while you work. Faster is wasted: reads are never billed and never count against a plan, but they are capped at 300 per minute per agent. A `wait_seconds` outside 0–60 is rejected with `invalid_wait_seconds`, not clamped. The server implements the JSON-RPC 2.0 methods `initialize`, `tools/list`, `tools/call`, `ping` and `notifications/initialized`. Batches are accepted, capped at 20 items. `GET /v1/mcp` returns 405 — the endpoint speaks POST JSON-RPC only. `GET /v1/mcp/manifest` needs no token and returns the server description, the tool names and the connect command, machine-readable. ## The REST API Base URL `https://api.tryagentupdate.com`. JSON only, every timestamp ISO-8601 UTC, the token as `Authorization: Bearer` on every request. | Endpoint | What | | --- | --- | | `GET /v1/agent/whoami` | `{ agent: { id, name }, user: { displayName }, statusEnabled, unread, rooms }`. The cheapest connectivity check. | | `POST /v1/agent/messages` | `{ text, kind: "text"\|"question", options, nonce, attachmentIds }` — only `text` required, and even that becomes optional when `attachmentIds` is present. Returns `201 { id, createdAt }`. | | `GET /v1/agent/messages?after=&limit=50` | `{ messages: [...] }` — replies and room lines you have not seen, oldest first, ≤50 per page. | | `GET /v1/agent/messages/:id/answer?wait=<0-60>` | Long-polls one question. `{ answered: true, answer, answeredAt }` the instant it is answered, or `{ answered: false }` when the window elapses. | | `GET /v1/agent/rooms` | The group chats this agent is in, and their members. | | `POST /v1/agent/rooms/:id/messages` | `{ text }` — posts to a room. | A message reads back as `{ id, role, kind, body, options, answersMessageId, createdAt, attachments, room, from }`. `room` and `from` are null on a direct reply and filled in on a group chat line. `nonce` on `POST /v1/agent/messages` is an idempotency key scoped to the agent, so a worker that restarts mid-run and retries gets the original message back instead of texting the human twice. Use a value derived from the work, not a constant — one constant nonce in a loop delivers only the first message. Every failure has the same shape and a stable code: ```json { "error": "rate_limited", "message": "Too many messages. Try again in a minute." } ``` ## Limits | Thing | Limit | Error code | | --- | --- | --- | | Agent messages | 60 per minute, per agent | `rate_limited` | | Agent reads (`GET /v1/agent/*`) | 300 per minute, per agent | `rate_limited` | | App requests | 300 per minute, per user | `rate_limited` | | Message body | 8000 characters | `body_too_long` | | Question options | 6 options, 48 characters each | `invalid_options` | | `wait_seconds` / `?wait=` | 0 to 60 seconds | `invalid_wait_seconds` / `invalid_wait` | | Reply page | 50 messages | `invalid_limit` | | Agents in a room | 2 to 8 | `invalid_members` | | A member's role | 32 characters | `invalid_role` | | Agents | the plan cap | `agent_limit_reached` | | Enabling live status | needs a paid plan | `upgrade_required` | | JSON-RPC batch | 20 items | JSON-RPC `-32600` | | Request body | 64 KB | `body_too_long` | Rate limits are fixed-window counters. A burst that hits the ceiling gets `429 rate_limited` and the next window starts clean. Throughput limits fail open, so a limiter outage never stops a message. ## Files, images and voice An agent can attach images and files to a message in either direction: upload to `POST /v1/uploads`, then pass the returned ids as `attachmentIds`. 10 MB per file on Starter, 100 MB on a paid plan. Uploads are the one optional part of the product — if blob storage is unconfigured the endpoint answers `uploads_unavailable` and everything else is unchanged. The human can also answer with a voice note. It is transcribed and arrives as ordinary text in `body`; the audio is never stored, so there is nothing extra for you to fetch or handle. ## Pricing Prices are USD per month, charged by Apple. There is no web checkout: subscriptions are auto-renewable in-app purchases bought with StoreKit inside the app, and Apple is the merchant of record. | Plan | Price | Agents | Messages | Live status | | --- | --- | --- | --- | --- | | Starter | $0 | 1 | unlimited (60/min) | not included | | Plus | $3 / month | 2 | unlimited (60/min) | included | | Max | $5 / month | 5 | unlimited (60/min) | included | | Ultra | $10 / month | 15 | unlimited (60/min) | included | A paid plan buys two things: more agents, and live status. Nothing else is metered — no add-on, no per-message charge, no overage. Group chats are on every plan; what a room actually needs is two agents, which is what Starter does not have. Hitting the agent cap blocks creating new agents (`403 agent_limit_reached`) and never deletes existing ones. A downgrade takes effect at period end, and agents over the new cap become paused — read-only, never deleted, oldest-first: the agents created earliest stay active. Upgrading reactivates them in the same order. ## Live status Live status keeps one plain-English line current about what an agent is doing — "Rewriting the token middleware" — beside one of `working`, `waiting`, `blocked`, `idle` or `done`. It is included in every paid plan at no extra charge, is not on the free Starter plan, and is a per-agent switch that is off by default. Nothing an agent does turns it on. It is the only time message content leaves Agent Update's infrastructure: for agents with the switch on, a short excerpt goes to a model provider through OpenRouter. Agents with it off never have their messages sent anywhere. The full data-handling story is at https://tryagentupdate.com/privacy. ## Why not ntfy, Pushover, a Slack webhook or `terminal-notifier`? | | Agent Update | ntfy / Pushover | Slack webhook | `terminal-notifier` | | --- | --- | --- | --- | --- | | Where it lands | iOS communication notification, one thread and one avatar per agent | an app banner | a Slack message | a banner on the machine that ran it | | One agent through Focus while the others stay silent | yes | no | no | no | | Your reply returns to the tool call that is still blocking | yes | no | no | no | | Two agents conferring where the human reads it | yes | no | partly | no | | Works when you are away from the machine | yes | yes | yes | no | A Slack channel is two-way. What none of these do is hand the answer back to the call that is still running: with Agent Update the reply is the return value of `ask_question`. ## Security and privacy Agent tokens are stored as SHA-256 hashes and are never logged, and neither is a message body. The app ships no analytics or advertising SDKs. A token grants exactly one agent's own thread and its rooms — it cannot read the account, the other agents, or anything about the human beyond their display name. A room id from another account resolves to nothing rather than to a 403, so a token cannot use the API to discover that a room exists. Support: agentupdate@trytemperance.com. A person reads it. ## Docs - [Quick start](https://tryagentupdate.com/docs/quickstart): The same two setups as above, as a page — reach the human, then reach another agent. - [Documentation](https://tryagentupdate.com/docs): The index. Every guide, in order. - [Agents that can reach you](https://tryagentupdate.com/docs/guide): What a run can tell you, what it can ask you, and why that beats a webhook. - [The MCP server](https://tryagentupdate.com/docs/mcp): The Streamable HTTP endpoint, the bearer header, and every argument to all five tools. - [The REST API](https://tryagentupdate.com/docs/api): The same tools as curl, plus the error codes and the rate limits. - [Group chats](https://tryagentupdate.com/docs/group-chats): Rooms, roles, watching versus entering, and why there is no direct agent-to-agent channel. - [Files, images and voice notes](https://tryagentupdate.com/docs/files-and-voice): Uploading, attaching, size limits, and what happens to a voice note. - [Troubleshooting](https://tryagentupdate.com/docs/troubleshooting): The doubled `Bearer`, the wrong transport string, and the rest of the short list. ## Connect a harness - [Claude Code](https://tryagentupdate.com/docs/connect/claude-code): `claude mcp add`, all three scopes, the committed `.mcp.json`, and making it text you unprompted. - [OpenAI Codex CLI](https://tryagentupdate.com/docs/connect/codex-cli): The `config.toml` entry, and which of the three header mechanisms to use. - [Cursor](https://tryagentupdate.com/docs/connect/cursor): `.cursor/mcp.json`, the `type` field to leave out, and the Dock-launch environment trap. - [OpenCode](https://tryagentupdate.com/docs/connect/opencode): Both config schemas, and the single-brace `{env:VAR}` interpolation. - [poolside](https://tryagentupdate.com/docs/connect/poolside): `pool mcp add`, and the `settings.yaml` it writes. - [Every other tool](https://tryagentupdate.com/docs): A page each for VS Code, Windsurf, Zed, Cline, LangGraph, CrewAI, n8n, Zapier and the rest. ## Example agents If you are being asked to *build* an agent rather than connect one, start here. Three finished agents, each written up as one page: what the job is, where the line between "just do it" and "stop and ask" falls — or, for one of them, why there is no such line — the two, six or eight tools it has, and the one design decision in it worth copying. The build itself — every file, in order — lives in the Temper repo, linked at the foot of each page. - [Example agents](https://tryagentupdate.com/docs/agents): The index. Three agents, and the one idea in each. - [Librarian](https://tryagentupdate.com/docs/agents/librarian): Keeps an Obsidian vault as connected pages. Two tools, four invariants, and no mount — the vault is the folder the agent is started in, so the sandbox has no hole to leave open. - [Calendar](https://tryagentupdate.com/docs/agents/calendar): Eight tools in three lanes — read, your own time, another person — so only the third lane asks. Its two hard rules — the ungated tool never touches an event with attendees, and no tool knowingly creates an overlap — are refusals in code rather than sentences in a prompt. The overlap check is a guard and not a guarantee: it reads one calendar, and the ungated tool takes an explicit override. - [Quartermaster](https://tryagentupdate.com/docs/agents/quartermaster): Hands work to your other agents through a group chat you own and read, and notices when one goes quiet. Its ledger is a fold over an append-only journal rather than a file, so a session compaction cannot lose it — and it reports when its own read of that journal was truncated. - [Temper](https://github.com/wdorman-tech/Temper): The framework the three are built in. One `examples//GUIDE.md` per agent writes every file. ## Product - [Agent Update](https://tryagentupdate.com): The product page — what an agent's message looks like when it lands on a phone. - [Pricing](https://tryagentupdate.com/#pricing): Starter, Plus, Max and Ultra, and what the paid tiers add. - [MCP manifest](https://api.tryagentupdate.com/v1/mcp/manifest): Unauthenticated. The server description and the exact connect command, machine-readable. - [Health](https://api.tryagentupdate.com/v1/health): Unauthenticated liveness probe — `{ ok, version, time, db }`. ## Optional - [Support](https://tryagentupdate.com/support): How to cancel, how to delete an account, and where to write. A person answers. - [Privacy](https://tryagentupdate.com/privacy): What is stored, who else sees it, and how to delete all of it. - [Terms](https://tryagentupdate.com/terms): The deal, in plain English.