Skip to content

The Outbox Pattern

Your agent doesn’t send emails directly. Instead, it writes files to an outbox directory, and the platform picks them up and sends them.

If the email API fails, the file stays in the outbox. The platform retries.

Every outgoing email is a file you can inspect. Nothing hidden.

The agent doesn’t need email credentials or API access. Just write a file.

The agent can’t access email APIs directly — it can only write files. This limits the blast radius of bugs.

Agent writes JSON → /data/outbox/email/
Queue worker reads outbox files
Sends via Resend API
Moves file to sent/ or failed/
/data/outbox/
├── email/ # Pending - agent writes here
├── sent/ # Successfully sent
└── failed/ # Failed to send (after retries)

Agent writes JSON files to /data/outbox/email/:

{
"to": ["[email protected]"],
"subject": "Re: Your question",
"body": "Here's what I found...",
"in_reply_to": "<[email protected]>",
"status": "pending"
}
FieldTypeDescription
tostring[]Recipient email addresses
subjectstringEmail subject line
bodystringPlain text email body
statusstringAlways "pending" when written
FieldTypeDescription
ccstring[]CC recipients
in_reply_tostringMessage-ID of email being replied to
referencesstringSpace-separated Message-IDs for threading
attachmentsarrayFile attachments (see below)

For proper email threading, include:

{
"in_reply_to": "<[email protected]>",
}
  • in_reply_to: The Message-ID of the email you’re replying to
  • references: Chain of Message-IDs in the thread

The platform passes these headers to Resend, which sets them correctly in the outgoing email.

Include files as base64-encoded attachments:

{
"to": ["[email protected]"],
"subject": "Report attached",
"body": "Please find the report attached.",
"attachments": [
{
"filename": "report.pdf",
"content": "JVBERi0xLjQK..."
}
]
}

The agent typically generates files in /data/ first, then base64-encodes them for the outbox.

Files in /data/outbox/email/ should have unique names. Common patterns:

  • {timestamp}.json — e.g., 1704379200000.json
  • {uuid}.json — e.g., a1b2c3d4-e5f6-7890-abcd-ef1234567890.json

The agent typically uses timestamps.

After the agent exits, the queue worker:

  1. Lists files in /data/outbox/email/
  2. Reads each JSON file
  3. Adds session footer to body
  4. Sends via Resend
  5. Moves to sent/ or failed/

Successfully sent emails are moved here with metadata added:

{
"to": ["[email protected]"],
"subject": "Re: Question",
"body": "...",
"status": "sent",
"sent_at": "2026-01-04T12:00:00Z",
"resend_id": "abc123"
}

Emails that failed after retries:

{
"to": ["[email protected]"],
"subject": "Re: Question",
"body": "...",
"status": "failed",
"error": "Invalid recipient address",
"failed_at": "2026-01-04T12:00:00Z"
}

The platform automatically appends a session footer to every outgoing email:

---
[buddy:a1b2 | ↑38 ↓5.0k R663k W37k $0.687 18.7%/200k | claude-haiku-4-5]

This is added during processing — the agent doesn’t need to include it.

You can view outbox contents:

  1. Ask your agent: “What files are in /data/outbox/”
  2. Check the dashboard file browser (coming soon)
  3. Export your R2 data