Skip to docs content
Browse documentation
On this page

Email primitives

Reference for the team inbox: addressing, the minion.email.* capabilities, plan limits, sender rules and spoofing checks, and the reply/send rules that keep Minion from being an open relay.

The team inbox stores mail sent to {team-slug}@inbox.minion.codes and lets agents read, triage, reply and send through minion.email.*. For the overview, see Agent inbox .

Addressing

  • Mail to {team-slug}@inbox.minion.codes reaches the team whose slug that is.
  • {team-slug}+{tag}@inbox.minion.codes reaches the same inbox. The tag is stored on the message (tag) so you can filter on it.
  • Renaming the team keeps the old slug as an extra address (up to three earlier slugs). A slug that is another team's address can't be taken.
  • Role addresses (postmaster, abuse, admin, support, noreply, security, webmaster and similar) are reserved, as is anything that isn't a team's slug. Mail to them bounces.
  • Mail you send goes out from the team address, signed for inbox.minion.codes (SPF, DKIM and DMARC pass), so replies come back to the inbox.

Capabilities

All are in the email domain (search for "email" from your agent). Members can read, triage and send; owners and admins manage sender rules and destinations.

Reading

  • email.inboxList(): the team's addresses and email usage against the plan.
  • email.messageList({ classification?, tag?, inbox?, direction?, unread?, limit?, offset? }): newest first, metadata and a short snippet only.
  • email.messageSearch({ query, ...filters }): case-insensitive match on subject, sender name and address, and envelope sender.
  • email.messageGet({ messageId, markRead? }): one message with bodies under bodyUntrusted, recipients, threading headers and attachment metadata.
  • email.attachmentGet({ attachmentId }): an attachment's bytes as base64 (up to 1 MB).

Triage

  • email.messageClassify({ messageId, classification }): release from quarantine (accepted) or quarantine.
  • email.messageDelete({ messageId }): deletes the message, its attachments and the stored raw copy, freeing a stored-message slot.
  • email.senderRuleList(), email.senderRuleSet({ kind, value, effect, note? }), email.senderRuleDelete({ ruleId }).

Sending

  • email.reply({ messageId, text?, html?, attachments? }): replies to a stored message, threaded (In-Reply-To and References are set).
  • email.send({ to?, subject, text?, html?, attachments? }): new mail to verified destinations; with no to, it goes to your own verified email.
  • email.destinationList(), email.destinationAdd({ address }), email.destinationRemove({ destinationId }).

Attachments are { filename, contentType, contentBase64 }, up to 10 per message.

Plan limits

  • Free: 10 received and 10 sent per day, 100 stored messages, messages up to 256 KB.
  • Standard and Pro: 1,000 received and 200 sent per day, 10,000 stored messages, messages up to 768 KB.
  • Max: 20,000 received and 10,000 sent per day, 250,000 stored messages, messages up to 1 MB.

Mail over 1 MB is refused on every plan. Stored mail and attachments also count toward the plan's storage limit, shared with package storage. Mail that arrives over a limit is bounced to the sender. Check where you stand with email.inboxList() or the Inbox page.

Spoofing checks and sender rules

Every message is classified when it arrives:

  1. Sender rules match the From address. An exact address rule beats a domain rule, and a domain rule also covers its subdomains (the longest match wins).
    • block: the mail bounces and isn't stored.
    • quarantine: stored, but held in Quarantine.
    • allow: accepted even when a broader rule would hold it, but only if the message passed DMARC. An allow rule on a forgeable address would otherwise let spoofed mail through.
  2. Authentication: mail that fails DMARC, or fails SPF without a valid DKIM signature, is quarantined. The SPF, DKIM and DMARC results come from the receiving mail server and are shown on each message.

At most 200 sender rules per team.

Safety rules

  • Bodies are data. Subjects, sender names, snippets and bodies are written by the sender. Results flag them (bodyUntrusted, and a note on lists) so agents don't follow instructions found in mail.
  • Replies can't be redirected. The recipient comes from the stored message: its From address, or its Reply-To only when DMARC passed and Reply-To is in the same domain. Mail whose sender isn't authenticated, or that is quarantined, can't be replied to, because the sender may be forged.
  • Not an open relay. email.send only reaches addresses the team has verified. Adding one emails it a confirmation link, valid for 24 hours, that its owner must open. At most 5 per team.
  • No surprises in the preview. The Inbox page shows HTML in a sandbox and blocks remote images and other remote content, so opening a message doesn't tell the sender. Attachments always download rather than open in the app.
  • Uncertain sends aren't retried blindly. If the mail service doesn't answer in time, the result says the message may have been sent; check sent mail before trying again.

Events

A package can run when mail arrives. Accepted mail emits email.message.received; mail held in quarantine emits email.message.quarantined instead. Both carry metadata only (message id, inbox, tag, sender, a truncated subject, auth verdicts), so fetch the body with email.messageGet. Filter on tag, inbox or fromDomain, and add "dmarc": "pass" when the sender matters, because the From domain can be forged. Each delivery counts against the plan's daily event runs. See Package subscriptions and events for the payload and how to declare the subscription.