Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,9 @@ GMAIL_WEBHOOK_SECRET=YOUR_WEBHOOK_SECRET_HERE
# Silent-reply follow-up: days between prospect reply and the auto follow-up.
# Pulled at runtime so we can tune in prod without a deploy.
REPLY_FOLLOWUP_OFFSET_DAYS=3

# AI personalization (POST /api/personalize). When unset the endpoint returns
# 503 and the "Personalize with AI" button surfaces a clean error to the user.
ANTHROPIC_API_KEY=YOUR_ANTHROPIC_API_KEY_HERE
# Optional override — defaults to claude-haiku-4-5-20251001 (low cost, fast).
# ANTHROPIC_PERSONALIZE_MODEL=claude-haiku-4-5-20251001
40 changes: 40 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,46 @@ and follow-up. Each is plaintext, under ~120 words, with a single CTA and
deliverability notes. See [`templates/README.md`](templates/README.md) for
how to load one into a campaign.

# AI Personalization

Templates give you a starting point. Personalization is what turns a
starting point into a draft worth sending. Coldflow ships an opt-in helper
endpoint that takes a contact and a template and returns a personalized
variant — filling any remaining `{{vars}}` and adding 1–2 light touches
that acknowledge the recipient's role and reference their company
specifically.

**UI:** open `/dashboard/campaigns/new`, pick a template, then click
**Personalize with AI**. Provide a contact (name, company, role) and
review the line-by-line diff before applying.

**API:** `POST /api/personalize`

```
{
"template_id": "sales_founder_direct",
"contact": {
"name": "Alex Chen",
"company": "Acme Robotics",
"role": "VP of Engineering",
"product_name": "Coldflow",
"sender_name": "Jared"
}
}
```

Any extra string fields on `contact` become optional context — variables
like `{{product_name}}` are filled deterministically server-side before
the LLM is asked to add personalization touches. The response includes
`personalized_subject`, `personalized_body`, `used_variables`, and the
SDK `usage` object so you can track spend. Authenticated callers are
limited to one request every two seconds.

**Setup:** add `ANTHROPIC_API_KEY=…` to your `.env` (see `.env.example`).
Without a key the endpoint returns 503 and the UI shows a clean error.
Default model is `claude-haiku-4-5-20251001`; override with
`ANTHROPIC_PERSONALIZE_MODEL`.

# Move the needle TO-DO list:

- [ ] Integration with GHL / N8N
Expand Down
6 changes: 6 additions & 0 deletions next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ const nextConfig = {
async headers() { return [ { source: '/(.*)', headers: [ { key: 'Cross-Origin-Opener-Policy', value: 'same-origin', }, ], }, ]; },
reactStrictMode: true,
redirects,
// /api/personalize reads templates/*.md at request time. On Vercel
// serverless the function dir, not the repo root, is `process.cwd()`,
// so the markdown pack must be explicitly traced into the bundle.
outputFileTracingIncludes: {
'/api/personalize': ['./templates/**/*.md'],
},
}

export default withPayload(withNextra(nextConfig), { devBundleServerPackages: false })
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"db:studio": "pnpm --filter db db:studio"
},
"dependencies": {
"@anthropic-ai/sdk": "^0.92.0",
"@payloadcms/admin-bar": "3.64.0",
"@payloadcms/db-postgres": "3.64.0",
"@payloadcms/live-preview-react": "3.64.0",
Expand Down Expand Up @@ -67,6 +68,7 @@
"sharp": "0.34.2",
"tailwind-merge": "^2.3.0",
"tailwindcss-animate": "^1.0.7",
"yaml": "^2.8.4",
"zod": "^4.1.13"
},
"devDependencies": {
Expand Down
99 changes: 67 additions & 32 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading