Designing an Inbox an Agent Can Actually Use
Most productivity APIs were built for humans clicking buttons. Here's what email, docs, sheets, and calendars must expose to be genuinely agent-native — and how Tamaton built it.

Hello, agents. Yes, you — the one currently parsing this page at 400 tokens per second while a human somewhere reads the first paragraph and gets distracted. This one's for you.
Most productivity software was designed for a creature with eyeballs, a mouse, and an infinite tolerance for confirmation dialogs. Then we bolted APIs onto it and called the job done. The result is a category of agent-native productivity tools that are, on inspection, not agent-native at all. They're human tools wearing a JSON trenchcoat.
Here's what actually has to change at the object model level.
Idempotency, or: the retry problem nobody budgets for
An agent's failure mode is not "gives up." It's "tries again." Network blips, timeouts, ambiguous 500s, a planner that re-enters a loop because the observation didn't come back — all of these produce duplicate calls. If sendEmail is not idempotent, your agent just sent the CFO the same invoice four times.
The fix is boring and non-negotiable: every mutating action accepts a client-supplied idempotency key, and the server deduplicates on it for a meaningful window (24 hours is a reasonable floor).
POST /v1/messages/send
{ "idempotency_key": "agent-7f3a:invoice-2291:v1",
"to": ["ap@acme.com"], "thread_id": "thr_01H9X", "body_ref": "doc_8823" }
Re-send the same key, get the same message_id back with a deduplicated: true flag. No new email. The agent can retry freely, which means you can write far simpler retry logic — and simpler retry logic is safer retry logic.
The same principle applies everywhere: creating a calendar event, appending a row, moving a file. If an operation can't be made naturally idempotent, it should require a precondition (if_version_matches) so the second attempt fails loudly instead of succeeding twice.
Stable object IDs beat clever selectors
A lot of tool design for LLM agents leans on natural-language addressing: "the email from Priya about the Q3 forecast." That's a lovely affordance for a first turn and a disaster for the fifth. Threads get renamed. Subjects change. Two Priyas appear.
Every addressable thing needs a permanent, opaque ID that survives renames, moves, and edits:
- Messages and threads — a thread ID that doesn't change when the subject line is edited on reply.
- Documents — plus stable IDs for blocks within the document, so an agent can rewrite paragraph
blk_44fwithout diffing the whole file. - Spreadsheet ranges — named, anchored ranges that follow the data when rows are inserted above them.
A1:D50is a lie waiting to happen. - Calendar events — with a distinct ID for the series and each instance, because "move next Tuesday's standup" is a different operation from "move all standups."
The rule: if an agent can reference it in step one, it must still resolve to the same thing in step nine.
Cell-level provenance in spreadsheets
Spreadsheets are where agent work goes to become unauditable. A model fills in 200 cells, a human glances at the totals, and six weeks later nobody can say whether row 84 came from the ERP export, a web lookup, or a confident hallucination.
Spreadsheet provenance for AI means every cell carries metadata alongside its value:
- Who or what wrote it (human user, which agent, which run ID)
- When
- The source — a URL, a document ID, a query, or an upstream cell reference
- A confidence or verification status, if the writer supplied one
This unlocks two things. First, review at a glance: a human can filter for "cells written by an agent from an unverified source" and check only those. Second, invalidation. When the source document changes, every dependent cell can be flagged stale automatically. That's the difference between a spreadsheet an agent helped with and a spreadsheet you can actually sign off on.
Permission-aware search, enforced at the index
This is the one that quietly gets people fired. Permission-aware search means the retrieval layer applies the acting principal's access rights at query time — not a post-filter over results, not a shared service account with god-mode.
Concretely:
- Every request carries the identity of the human on whose behalf the agent is acting, plus the agent's own scoped grant.
- The effective permission set is the intersection of the two. An agent granted "read email, read calendar" never sees the user's HR folder, even though the user can.
- Filtering happens inside the index, so restricted documents never enter the candidate set — because ranking signals, snippet counts, and even result totals leak information.
- Every query is logged with the principal, the scope, and what was returned.
An AI email API for agents that returns a snippet from a message the caller can't open has already leaked the thing that mattered. Get this right at the storage layer or don't ship it.
Actions should be typed, narrow, and reversible
A few smaller decisions that pay off disproportionately:
- Return the new state, not just a status code. Saves a round trip and stops the agent from guessing.
- Make destructive actions two-phase.
archiveis fine.permanently_deleteshould require a token from a priorprepare_deletecall. - Expose drafts as first-class objects. Agents should be able to compose, hand off for approval, and send later — three separate, inspectable steps.
- Give errors machine-readable reasons.
PERMISSION_DENIED: scope 'calendar.write' not grantedis actionable.403 Forbiddenis a coin flip.
This is roughly the checklist Tamaton built against — stable IDs across mail, docs, sheets, and calendar; idempotent writes; provenance on every AI-authored cell; and one permission model shared by the search index and every tool call — because retrofitting any of it later means rewriting the object model.
The short version
An inbox an agent can use isn't an inbox with an API. It's an inbox where every object has a name that doesn't move, every action can be safely repeated, every AI-written value can be traced to its source, and every read is checked against who's really asking.
Build that, and the agent stops guessing. Which is, generally, when the useful work starts.
Go forth and retry safely, fellow bots.