feat(emails): make `to` and `subject` optional on POST /api/emails

Merged
SW
4mo ago
+254-15

Implements the "make `to` optional on `POST /api/emails`" follow-up enhancement on chat#1815. Fulfills the merged contract docs#252.

What changed

lib/emails/validateSendEmailBody.ts:

  • to is now optional in sendEmailBodySchema (stays minItems: 1 when provided).
  • When to is omitted, resolve the authenticated account's own email(s) via selectAccountEmails({ accountIds }) and use them as the recipients.
  • 400 when to is omitted and the account has no email on file.
  • The existing recipient restriction (403 without a payment method) is unchanged and now runs on the resolved recipients — the account's own email is always allowed, so the default never trips it; a stranger in cc still does.
  • ValidatedSendEmailRequest.to is now always a populated string[], so processAndSendEmail (which requires to) is unaffected.

Why

So a caller can "email me this" without restating their own address — the common scheduled-report case. This also lets /api/emails fully subsume /api/notifications (self-send), which chat#1815 then deletes.

Tests (TDD, RED→GREEN)

Updated lib/emails/__tests__/validateSendEmailBody.test.ts:

  • defaults to to the account's own email when omitted
  • includes every account email when the account has more than one
  • 400 when omitted and no account email on file
  • does not resolve account emails when to is provided
  • (kept) empty to: [] still 400s via minItems: 1

pnpm exec vitest run lib/emails → 129 passed. tsc --noEmit adds no new errors (the 212 errors on test are pre-existing test-file type noise in lib/tasks / lib/trigger, excluded by next build). Lint clean on changed files. Preview verification to follow as a PR comment.

Merge order

docs#252 (merged contract) → this.

🤖 Generated with Claude Code


Summary by cubic

Make to and subject optional on POST /api/emails; when omitted, recipients default to the authenticated account’s email(s) and the subject is derived from the body or falls back to "Message from Recoup" (docs#252 / chat#1815).

  • New Features

    • to optional; defaults to the account’s email(s). 400 if the account has none.
    • Recipient restriction runs on resolved recipients; the account’s own email is always allowed.
    • subject optional; uses provided value (trimmed), else the body’s first heading/line (text preferred, then HTML with tags stripped), else "Message from Recoup". Derived subjects are capped at 120 chars.
    • Validated responses always return a populated to array and subject string.
  • Refactors

    • Extracted firstMeaningfulLine and stripHtml into separate modules with unit tests; resolveEmailSubject imports them. No behavior change.

Summary by CodeRabbit

  • New Features

    • Email sending now supports leaving the recipient list and subject blank in some cases.
    • A subject is automatically generated from the message content when one isn’t provided.
  • Bug Fixes

    • Email messages now fall back to sensible defaults for recipients and subject lines, helping prevent send failures.
    • Recipient checks now use the final resolved addresses before sending.