Skip to content

Email Resource

The email: resource sends outbound email via SMTP and reads or searches inbound messages via IMAP. Use it to deliver notifications, reports, and alerts from any workflow step.

Where it runs

Both workflow mode and agent mode.

Actions

Set action: to one of four values:

ActionWhat it does
send (default)Send an email via SMTP
readRetrieve recent messages from an IMAP mailbox
searchSearch messages in an IMAP mailbox by criteria
modifyChange flags or move/delete messages via IMAP

Global Named Connections

SMTP and IMAP credentials belong in ~/.kdeps/config.yaml, not in workflow.yaml. Resources reference connections by name. This keeps all secrets in one machine-local file and out of version-controlled workflow files.

yaml
# ~/.kdeps/config.yaml
smtp_connections:
  default:
    host: "${SMTP_HOST}"      # e.g. smtp.gmail.com
    port: 587
    username: "${SMTP_USER}"
    password: "${SMTP_PASS}"
    tls: false                # false = STARTTLS on 587, true = implicit TLS on 465

imap_connections:
  inbox:
    host: "${IMAP_HOST}"      # e.g. imap.gmail.com
    port: 993
    username: "${IMAP_USER}"
    password: "${IMAP_PASS}"
    tls: true

Sending Email

yaml
# resources/notify.yaml
actionId: notify
requires: [llm]
email:
  action: send
  smtpConnection: default   # references smtp_connections.default in ~/.kdeps/config.yaml
  from: "reports@example.com"
  to:
    - "alice@example.com"
  subject: "Daily Report"
  body: "{{ get('llm') }}"

HTML email — set html: true and put HTML in body::

yaml
email:
  action: send
  smtpConnection: default
  from: "noreply@example.com"
  to: ["{{ get('recipient') }}"]
  subject: "Your Report"
  body: "<h1>Summary</h1><p>{{ get('llm') }}</p>"
  html: true

With attachments:

yaml
email:
  action: send
  smtpConnection: default
  from: "reports@example.com"
  to: ["cfo@example.com"]
  subject: "Q3 Report"
  body: "See attached."
  attachments:
    - "/data/reports/q3.pdf"

Output (send)

json
{"success": true, "action": "send", "from": "...", "to": [...], "subject": "..."}

Reading Email

yaml
# resources/check-inbox.yaml
actionId: checkInbox
email:
  action: read
  imapConnection: inbox   # references imap_connections.inbox in ~/.kdeps/config.yaml
  mailbox: "INBOX"
  limit: 10
  markRead: true

Output (read)

An array of message objects:

json
[
  {
    "uid": "42",
    "subject": "New order #1234",
    "from": "orders@shopify.com",
    "to": ["ops@example.com"],
    "date": "2024-03-15T09:00:00Z",
    "body": "Order details...",
    "html": ""
  }
]

Access fields with get('checkInbox')[0].subject, get('checkInbox')[0].body, etc.

Searching Email

yaml
# resources/find-orders.yaml
actionId: findOrders
email:
  action: search
  imapConnection: inbox
  mailbox: "INBOX"
  limit: 50
  search:
    from: "orders@shopify.com"
    subject: "New order"
    unseen: true
    since: "2024-01-01"

Search fields: from, to, subject, body, since (ISO date), before (ISO date), unseen (bool), flagged (bool).

Modifying Messages

yaml
# resources/archive.yaml
actionId: archive
email:
  action: modify
  imapConnection: inbox
  mailbox: "INBOX"
  uids:
    - "{{ get('findOrders')[0].uid }}"
  modify:
    markSeen: true
    moveTo: "Processed"

Output (modify)

json
{"success": true, "modified": 1}

Configuration Reference

smtp_connections fields (in ~/.kdeps/config.yaml)

FieldTypeDescription
hoststringSMTP server hostname
portintPort (default: 465 for TLS, 587 for STARTTLS)
usernamestringAuth username
passwordstringAuth password
tlsbooltrue = implicit TLS (port 465), false = STARTTLS (port 587)
insecureSkipVerifyboolSkip TLS certificate verification (dev only)

imap_connections fields (in ~/.kdeps/config.yaml)

FieldTypeDescription
hoststringIMAP server hostname
portintPort (default: 993 for TLS, 143 for plain)
usernamestringAuth username
passwordstringAuth password
tlsboolEnable TLS
insecureSkipVerifyboolSkip TLS certificate verification (dev only)

Top-level email: fields

FieldTypeDefaultDescription
actionstringsendsend, read, search, or modify
smtpConnectionstringNamed SMTP connection (required for send)
imapConnectionstringNamed IMAP connection (required for read/search/modify)
fromstringSender address (send only)
to[]stringRecipients (send only)
cc[]stringCC recipients (send only)
bcc[]stringBCC recipients (send only)
subjectstringSubject line (send only)
bodystringPlain-text or HTML body (send only)
htmlboolfalseTreat body as HTML (send only)
attachments[]stringLocal file paths to attach (send only)
mailboxstringINBOXMailbox for read/search/modify
limitint10Max messages to return (read/search)
markReadboolfalseMark retrieved messages as read
uids[]stringMessage UIDs to target (modify)
searchobjectSearch criteria (search action)
modifyobjectModification flags (modify action)
timeoutstring30sOperation timeout

modify: fields

FieldTypeDescription
markSeen*boolSet or clear \Seen flag
markFlagged*boolSet or clear \Flagged flag
markDeleted*boolSet or clear \Deleted flag
moveTostringMove messages to this mailbox
expungeboolPermanently delete messages marked for deletion

Secrets

Always use environment variables -- never hardcode credentials:

yaml
# ~/.kdeps/config.yaml
smtp_connections:
  default:
    host: "${SMTP_HOST}"
    username: "${SMTP_USER}"
    password: "${SMTP_PASS}"
imap_connections:
  inbox:
    host: "${IMAP_HOST}"
    username: "${IMAP_USER}"
    password: "${IMAP_PASS}"

Gmail: Use an App Password, not your account password. SMTP: smtp.gmail.com:587 with tls: false (STARTTLS). IMAP: imap.gmail.com:993 with tls: true.

Common Patterns

Send a report after LLM generation

yaml
# ~/.kdeps/config.yaml
smtp_connections:
  reports:
    host: "${SMTP_HOST}"
    port: 587
    username: "${SMTP_USER}"
    password: "${SMTP_PASS}"
    tls: false

# resources/send-report.yaml
actionId: sendReport
requires: [generateReport]
email:
  action: send
  smtpConnection: reports
  from: "${REPORT_FROM}"
  to: ["${REPORT_TO}"]
  subject: "Weekly Summary - {{ get('week') }}"
  body: "{{ get('generateReport') }}"

Poll inbox and process new messages

yaml
# resources/poll.yaml
actionId: poll
email:
  action: search
  imapConnection: inbox
  search:
    unseen: true
  limit: 20

onError fallback for SMTP failures

yaml
email:
  action: send
  smtpConnection: default
  from: "alerts@example.com"
  to: ["ops@example.com"]
  subject: "Alert"
  body: "Something happened."
onError:
  action: continue
  fallback: {"success": false, "message": "email delivery failed"}

Released under the Apache 2.0 License.