Auggyv0.5.0
PublishedDocs / Augments
AgentMail

Give an agent a policy-gated inbox with durable inbound delivery, send, reply, and forward tools, creator review, and recovery.

What it enables

What it enables

Incoming email can wake the agent. The agent can send, reply, or forward under trust, recipient, content, rate, and review policies you control.

Status
Stable
Availability
Available in `[email protected]`. Inbound email is off by default; enable polling, WebSocket, or verified webhook delivery with an explicit sender allowlist.

Contributes

toolstransportroutesSQLite ledgeradmin controls

Use AgentMail for email conversations

Use this augment when incoming email should become an agent turn or the agent needs to continue a thread. Use `notify` for one-way alerts and `visitorAuth` only for magic-link delivery. If you need occasional operator-driven mail actions without an inbox in the runtime, AgentMail MCP may be simpler.

Install and configure

  1. 1Run `auggy augment add agentMail` from the agent project.
  2. 2Run `auggy augment setup agentMail`, or set `AGENTMAIL_API_KEY` and `AGENTMAIL_INBOX_ID` in `.env`.
  3. 3To receive email, choose one inbound mode and add a non-empty exact/domain sender allowlist.
  4. 4If public email turns may reply, add `public` to `outbound.allowedTrustLevels` and keep it in `humanReview.requiredForTrustLevels` so replies queue for creator approval.
  5. 5Keep `webTransport` mounted. Webhook mode needs its verified route; executable human review needs the creator admin route.
.env.env
AGENTMAIL_API_KEY=am_replace_me
AGENTMAIL_INBOX_ID=[email protected]
# Webhook mode only:
AGENTMAIL_WEBHOOK_SECRET=whsec_replace_me
augments/agentMail/augment.yamlyaml
type: agentMail
config:
  apiKey: ${AGENTMAIL_API_KEY}
  inboxId: ${AGENTMAIL_INBOX_ID}
  dbPath: ./agent-mail.db
  outbound:
    allowedTrustLevels:
      - creator
      - public
    allowedRecipients:
      - "*@customer.example"
    subjectPrefix: "[Auggy] "
    maxRecipients: 10
    bodyMaxBytes: 102400
    allowHtml: false
    rateLimit:
      globalMaxPerHour: 10
      perRecipientCooldownMs: 300000
      dedupWindowMs: 300000
    humanReview:
      requiredForTrustLevels:
        - public
      expiresAfterMs: 86400000
  inbound:
    mode: websocket
    allowedSenders:
      - "*@customer.example"
    pollIntervalMs: 60000
    maxPromptBytes: 102400
    maxAttempts: 5
    classifications:
      received: process
      spam: discard
      blocked: discard
      unauthenticated: discard

Webhook mode

Replace `mode: websocket` with `mode: webhook` and optionally set `webhook.path`, `webhook.secretEnv`, and `webhook.timestampToleranceSeconds`. Defaults are `/webhooks/agentmail`, `AGENTMAIL_WEBHOOK_SECRET`, and 300 seconds.

Choose and admit the arrival path

ModeLive arrivalDurable recoveryUse when
noneDisabledNo inbound ledger workerThe agent only sends new email
pollingPeriodic REST readsCheckpointed reads are the arrival and catch-up pathA simple one-minute default cadence is enough
websocketReconnectable AgentMail subscriptionREST catch-up runs after subscriptionYou need low latency without a public callback
webhookSvix-verified POST routeREST catch-up runs at boot; the ledger deduplicates provider retriesThe agent has a stable public URL

Every enabled mode drains the same SQLite ledger. Provider delivery and message IDs are deduplicated; catch-up starts with a 24-hour first-run lookback and advances a checkpoint with one minute of overlap; workers lease one message, retry failed turns with bounded backoff, and durably mark processed or discarded outcomes.

One inbound owner per inbox

Only one enabled inbound AgentMail augment may own an inbox. Multiple outbound-only instances can coexist, but competing inbound workers are rejected.

GateDefault behavior
SenderEnabled inbound requires `allowedSenders`; exact addresses and `*@domain` patterns compare case-insensitively.
ClassificationOrdinary received mail is processed. Spam, blocked, and unauthenticated events are discarded unless explicitly overridden.
IdentityThe sender becomes a deterministic `public` + `anonymous` peer scoped to the inbox, sender, and thread.
PromptThe provider envelope is rendered as bounded, explicitly untrusted JSON. Email content never becomes a system instruction.
FailureA failed turn retries up to `maxAttempts` (default 5), then the ledger durably discards it.

Send, reply, forward, and review

ToolUseImportant boundary
send_messageStart a new messageTrust, recipient, content, rate, dedup, and review policy all run before the provider call
reply_to_messageContinue the triggering email threadThe ID must have been delivered in this turn; real reply recipients are rechecked against policy
forward_messageHand the triggering message to another recipientThe ID must have been delivered in this turn and forward recipients are policy-checked

A configured review trust level receives `status: "pending_review"`, not `sent`. The exact queued request is stored with an immutable fingerprint and expires after 24 hours by default. The redacted admin row links to a creator-authenticated, `no-store` detail route; approval requires its fingerprint and rechecks current rate limits.

Sensitive content is flagged, not rewritten

Auggy scans outbound text for token-shaped secrets and marks matches in the operator audit surface. It does not block or rewrite the message; use trust, recipient, and review policies to decide whether it can be sent.

Never retry an ambiguous send

AgentMail sends do not accept an idempotency key. When the provider may have accepted a request before the connection failed, Auggy marks the durable attempt ambiguous. Inspect AgentMail and reconcile it as sent or not sent through the admin action; blind retry can duplicate real email.

Persistence, operations, and limits

Locally, relative AgentMail state resolves from the agent project. On Railway it is namespaced under `/app/data/agent-mail/<augment-name>`. Startup requires the real `/app/data` volume, creates the AgentMail state directory with mode 0700, and proves fsync plus atomic rename durability before the agent starts.

  • Inspect inbound mode/live state, ledger counts, checkpoint, catch-up summary, last event, worker outcome, and provider error.
  • Inspect redacted dispatch and review rows; exact review content requires creator authentication.
  • Send a test message, adjust the hourly cap, approve or reject queued mail, and reconcile ambiguous attempts.
  • Do not add root-level SQLite symlinks. Core stores resolve directly to the Railway volume; only Link retains a compatibility symlink.
  • Outbound attachments are not exposed.
  • Drafts, read receipts, retraction, and delivery-event turns are not exposed.
  • An inbound turn receives the triggering message, not an automatically fetched full thread transcript.
  • The `notify` AgentMail adapter and `visitorAuth` magic-link delivery remain separate outbound-only concerns.

Verify the inbox

  1. 1Run `[email protected]` and confirm the inbox healthcheck and inbound runtime are ready in the creator admin surface.
  2. 2Send one allowed ordinary email and confirm it becomes a public anonymous turn exactly once.
  3. 3Restart the agent, send mail while it is offline, and confirm REST catch-up processes the missed message without duplicating the earlier one.
  4. 4Send mail from an off-allowlist sender and a discarded classification; confirm neither reaches the model and both outcomes are visible in ledger counts.
  5. 5From an admitted public email turn, propose a reply. Confirm the tool returns `pending_review`, inspect the exact request as creator, then approve with the matching fingerprint.
  6. 6On Railway, confirm the volume is mounted at `/app/data` and AgentMail state lands beneath `/app/data/agent-mail/<augment-name>` after redeploy.

Related