Send and receive mail, with credentials kept in Supabase Vault.
SupaNet can send and receive email so that agents and users can act over mail -
the "morning agent emails me a summary" pattern, or an inbox an agent can read.
Two seeded built-in tools, send_email and check_email, become available once
an admin configures a provider in Settings → Email.
Sending
Sending goes through an HTTP provider (such as Postmark or Resend), not raw SMTP.
Because sending is exfiltration-capable, send_email stays an ordinary tool row,
so all the usual controls apply: admin activation, per-agent tool_ids scoping,
and the webhook allow_tools gate. On top of that it adds:
- an optional recipient allowlist (an exact address or an
@domainsuffix), - a rate limit (20 sends per hour), and
- an
email.sentactivity-log entry for every send.
Recipient allowlist
The allowlist restricts which email addresses agents and webhooks can send to.
When empty, send_email can reach any address — a data exfiltration risk when
combined with untrusted input (like a user-submitted email address in a form).
The Settings → Email page shows a security warning if an email provider is
configured but the allowlist is blank, reminding admins to set allowed recipients.
An @domain rule in the allowlist matches addresses ending with that exact string.
For example, @example.com matches user@example.com but not user@sub.example.com
(which ends with .example.com, not @example.com). To allow subdomains, add each
subdomain as its own rule — the safe default for a send-to allowlist.
Receiving
SupaNet supports two email ingestion paths:
Inbound-parse (provider push)
The provider POSTs each incoming message to the public email-inbound edge function
(token-gated like a webhook), which normalizes it into the inbox_messages table.
check_email then reads that table — so it is push, not polling.
IMAP polling (user mailboxes)
Members can register IMAP mailboxes (via Inbox → Inboxes → Add inbox) and the
workspace automatically polls them. The email-poll edge function is cron-ticked
every minute but respects each mailbox's poll_interval_minutes setting (1–1440
minutes; default 5). It:
- Opens an IMAP TLS connection to the mailbox's host.
- Searches for messages with a UID greater than the last one ingested.
- On a fresh inbox, backfills only the ~10 most recent messages.
- Parses each message's RFC822 (headers, MIME body, base64/quoted-printable transfer encodings).
- Dedupes on
Message-ID. - Records any connection or parsing errors on the account's
last_error.
The mailbox password lives only in Supabase Vault (same pattern as the email
provider key). New mail lands in inbox_messages (source='email') and fires
message.received events, so listeners can route them like any other message source.
Testing without waiting
The inbox editor has two testing buttons:
- Test connection — the
imap-testedge function (verify_jwt=true, signed-in users only) logs in and reports the folder's message count, so you can validate an app password before saving. - Test message — inserts a synthetic
source='email'row and firesmessage.received, letting you wire and verify a listener end-to-end without waiting for real mail.
A fresh inbox is also polled on the next 1-minute tick instead of waiting for its full interval, since it has no last_checked_at yet.
The pure RFC822 parser lives in supabase/functions/_shared/imap.ts and is
unit-tested in tests/imap_test.ts.
Where credentials live
Email credentials live only in Supabase Vault. The non-secret config sits in
a public.integrations row with a pointer to the secret; the actual key is never
a table column, never in a client payload, and never logged.
The flow is tight:
- The client writes the key solely through an admin-gated, security-definer RPC
(
set_email_integration), which checks admin status in its body. - Edge functions read the decrypted key only through a service-role-only RPC
(
read_email_secret).
Runs everywhere
send_email and check_email execute through the shared built-ins module, so
they work identically in all three loops - chat, webhook, and scheduler. That is
what makes a scheduled agent able to email you without any special wiring.