Guild

Ops — Email (Resend)

Transactional email runs through Resend from `lib/email/client.ts`. All notification emails are sent server-side from API routes using React Email templates.

Transactional email runs through Resend from lib/email/client.ts. All notification emails are sent server-side from API routes using React Email templates.

The sender

  • notifications@mail.greenbook.legal — verified Resend domain (verified 2026-08-06, pass 11). This supersedes the BUILD-SPEC §7.4 guild.app placeholder, which is not a registered/verified domain.
  • Free-tier cap: ~100 emails/day (Resend free tier). The platform sends at most one email per notification event (7 event types), so this is ample for the MVP — but a seed script or demo loop that proposes many matches will exhaust the daily budget. Watch the Resend dashboard if emails suddenly stop.

What sends what (7 triggers, 7 templates)

TriggerTemplateRecipient
POST /api/matches (match proposed)match-proposedthe practitioner receiving the proposal
PATCH /api/matches/[id] → acceptedmatch-acceptedthe proposer
PATCH /api/matches/[id] → declinedmatch-declinedthe proposer
POST /api/messagesnew-messagethe other participant in the match
POST /api/referralsreferral-proposedthe referral recipient
PATCH /api/referrals/[id] → acceptedreferral-acceptedthe referral sender
POST /api/bidsbid-approval-neededthe case owner

Templates live in lib/email/templates/; styles.ts is the shared layout. tests/email-render.test.ts verifies all templates render.

How sending works

// lib/email/client.ts — constructed lazily so the app builds without a key
await sendEmail(to, subject, <Template />);

Key properties:

  • No-op safe. Without RESEND_API_KEY, sendEmail logs a warning and returns — the app builds and runs with no email configured (pass-11 verified the silent no-op path).
  • Fail-soft. A Resend error logs Failed to send email and does not fail the request — a 500 from an email provider never fails the API call that triggered it.
  • appUrl(path) builds absolute links from NEXT_PUBLIC_APP_URL (local: http://localhost:3000; prod: https://guild.pages.dev).

Debugging

  • Emails missing? Check the Resend dashboard for send status, then confirm RESEND_API_KEY is set in the environment that actually ran the request (.env.local for dev, Cloudflare Pages dashboard for prod) and that the daily cap isn't exhausted.
  • Test receipts: pass-11 used reserved-domain addresses (e.g. test-pass10-b@example.com — no MX, harmless).
  • Domain change (pass 13, optional): verify the new domain in Resend and update the from address in lib/email/client.ts.

On this page