Any mailbox: you bring the SMTP transport and IMAP poller for your runtime.
Install
npx shadcn@latest add @tendkit/channel-smtpIt lands in lib/support/channels/. Import smtpChannel from there.
Set up
Pick your network libraries
The adapter builds and parses MIME; you inject two functions:
smtp.send(raw, envelope)andimap.fetchSince(state). On Node, nodemailer and imapflow work well. On Cloudflare Workers use port 587 or 465 (25 is blocked), with nodemailer or worker-mailer for SMTP and imapflow for IMAP.Track position by UID
Store the mailbox's UIDVALIDITY and next UID in the returned state. On the first call return no messages, only the current position, so old mail isn't imported.
Run the schedule
runScheduled()calls your IMAP poller. Keep each run short; return at most a batch of messages.
Configure
smtpChannel({
id: "email",
address: "support@acme.com",
smtp: {
send: (raw, envelope) =>
transporter.sendMail({ raw: Buffer.from(raw), envelope }).then(() => {}),
},
imap: { fetchSince: (state) => pollInbox(state) },
})Options
| Name | Type | Description |
|---|---|---|
id* | string | Stable channel id, stored on every ticket and used in the webhook URL. Never change it. |
address* | string | The address customers write to and replies come from. |
smtp* | { send(raw, envelope) } | Delivers one complete RFC 5322 message. |
imap | { fetchSince(state) } | Reads the inbox. Leave out when mail arrives another way. |
replyAddress | string | An address that routes back to this mailbox, for the ticket token. |
Behaviour
- Works with Fastmail, Zoho, cPanel hosting, or Gmail and Microsoft 365 with an app password.