CRM Feature Plan · crm-v1.3
Send targeted email campaigns to customer segments directly from the admin app. Campaigns pick up where segments leave off — define your audience in Segments, compose a message, hit Send. This plan covers the three email configuration approaches and recommends the right one for Setpoint.
Overview
Problem
Facility operators have no way to communicate with customer segments. They can identify high-value customers, at-risk members, and inactive players — but there is no action attached to that insight. The data sits idle.
Solution
Email campaigns inside the admin CRM. Compose once, target a segment (or everyone), send. Results tracked per recipient. Merge tags personalise subject and body. No third-party marketing tool required.
Already Built
Unresolved
Where should the Resend API key live — in-app per-facility settings (already wired up), a shared platform env var, or Supabase Vault? Each has different security, UX, and operational trade-offs.
Email Configuration
The core question: where does the Resend API key live? Each option has different security, UX, and operational implications.
Each facility enters its own Resend API key inside the admin app
How it works
Facility admin opens CRM → Campaigns → Settings dialog. They paste their own Resend API key and configure a from-name + from-email. The key is stored in crm_email_settings (server-side only, never returned to frontend). Every send uses that facility's key, so emails come from their own Resend account.
Pros
Cons
Verdict
Recommended for Setpoint. Multi-tenant SaaS should not share an email sending account across facilities — domain reputation bleeds between them. Per-facility keys are the industry standard for platforms like this (Mailchimp embedded, Klaviyo agency mode, etc.).
Single Resend API key owned by Setpoint, all facilities share it
How it works
Add RESEND_API_KEY to the admin app's environment variables on Vercel. All campaigns from all facilities use this one key. Emails appear to come from a Setpoint-owned domain (e.g., campaigns@setpoint.app). Facility operators set nothing — it just works.
Pros
Cons
Verdict
Only suitable for a single-tenant setup or internal demos. Not recommended for a real SaaS with multiple facilities. Shared sending reputation is a liability at scale.
Same as Option A but keys encrypted at rest using Supabase Vault (pgsodium)
How it works
Instead of storing the Resend API key as plaintext in crm_email_settings, encrypt it via Supabase Vault before insert. On send, decrypt using vault.decrypted_secrets or pgsodium.crypto_secretbox_open(). The facility admin UX is identical to Option A.
Pros
Cons
Verdict
Overkill for v1.3. The current approach (Option A) already keeps keys server-side only — they're never returned to the frontend. Vault adds value if the DB itself is at risk, but that's a concern for later. Upgrade path: migrate to Vault in v2.x without changing the operator UX.
Domain Configuration
Resend provides a shared sending domain for testing. Works immediately, no DNS setup. Not suitable for production — often filtered as spam.
Best for: Local dev and testing onlyFacility verifies their domain in Resend (adds 3 DNS records: SPF, DKIM, DMARC). Best deliverability, fully branded. Resend dashboard guides setup in ~10 minutes.
Best for: Production — recommendedSetpoint owns a sending subdomain and grants use to facilities. Simpler for operators but ties reputation to Setpoint. Requires Setpoint to manage DNS.
Best for: Optional — if operator cannot do DNS setupCampaign Flow
Facility admin enters Resend API key + from name + from email once. Stored server-side. Never shown again — only a hasKey indicator displayed.
Location: CRM → Campaigns → Settings (gear icon)
Name the campaign, pick a target (All Customers or a saved Segment), write subject + body. Merge tags: {{first_name}}, {{full_name}}, {{email}}, {{facility_name}}.
Location: CRM → Campaigns → New Campaign
Recipient count shown before sending — pulled from the selected segment's live evaluation. Operator knows exactly how many emails will go out.
Location: Campaign card (audience line shows count)
Operator clicks Send, confirms in a dialog. Server resolves recipients, applies merge tags per person, calls Resend REST API in a loop, records results.
Location: Campaign card → Send button → Confirm dialog
After sending: total sent, failed count, error details for failed recipients. Campaign status flips to Sent. Drill-in to see per-recipient log.
Location: Campaign card shows sent/failed badges
Personalisation
Use these in subject and body to personalise per recipient. Unrecognised tags are left as-is.
| Tag | Resolves To | Example |
|---|---|---|
| {{first_name}} | Customer's first name | Somchai |
| {{full_name}} | Customer's full name | Somchai Rattanakorn |
| {{email}} | Customer's email address | somchai@example.com |
| {{facility_name}} | Facility name from DB | Pad Thai Padel |
For Nestor
Which email configuration option do you prefer — A (per-facility Resend key), B (shared platform key), or C (Vault)?
Recommendation: Option A — already built, industry standard for multi-tenant SaaS, zero Setpoint billing exposure.
Impact: Determines if we need to rebuild the email settings flow or proceed as-is.
Should campaigns support HTML email (rich formatting) or plain text only?
Recommendation: Start with plain text — simpler, no template builder needed, high deliverability. Add HTML template editor in v1.4.
Impact: HTML requires a template builder UI and HTML rendering in Resend — significant extra scope.
Do we need scheduled/scheduled campaigns (send at a specific time) or is immediate send enough for v1.3?
Recommendation: Immediate send for v1.3. Scheduled sends via pg_cron in v1.4.
Impact: Scheduled campaigns require a status column update + pg_cron job per campaign.
Should we track email opens and clicks (requires Resend webhook integration)?
Recommendation: Defer to v1.4. Resend webhooks require an inbound endpoint and more DB schema. Sent/failed tracking is enough for v1.3.
Impact: Open/click tracking = Resend webhooks + new DB columns + a new analytics tab.
Bottom Line
The per-facility Resend key approach (Option A) is already fully implemented. It is the correct architecture for multi-tenant SaaS — each facility is isolated, Setpoint has no billing exposure, and domain reputation is per-facility. The operator setup is a 5-minute one-time task (sign up for Resend free plan, add DNS records, paste key). All the core campaign infrastructure is built. The right next step is to apply the migration, test end-to-end with a real Resend key, and ship.
Immediate Actions
Deferred to v1.4