1- // apps/website/src/app/api/whitepaper-signup/route.ts
21import { NextRequest , NextResponse } from 'next/server' ;
32import fs from 'fs' ;
43import path from 'path' ;
4+ import { sendEmail , FROM , addToAudience } from '../../../../lib/resend' ;
5+ import { loopsUpsertContact , loopsSendEvent } from '../../../../lib/loops' ;
6+ import { scheduleWhitepaperDrip , type PaperId } from '../../../../lib/drip' ;
7+ import { whitepaperDownloadHtml } from '../../../../emails/whitepaper-download' ;
8+ import { angularDownloadHtml } from '../../../../emails/angular-download' ;
9+ import { renderDownloadHtml } from '../../../../emails/render-download' ;
10+ import { chatDownloadHtml } from '../../../../emails/chat-download' ;
511
612const SIGNUPS_FILE = path . join ( process . cwd ( ) , 'data' , 'whitepaper-signups.ndjson' ) ;
713
14+ const VALID_PAPERS : PaperId [ ] = [ 'overview' , 'angular' , 'render' , 'chat' ] ;
15+
16+ const DOWNLOAD_EMAILS : Record < PaperId , ( name ?: string ) => string > = {
17+ overview : whitepaperDownloadHtml ,
18+ angular : angularDownloadHtml ,
19+ render : renderDownloadHtml ,
20+ chat : chatDownloadHtml ,
21+ } ;
22+
23+ const DOWNLOAD_SUBJECTS : Record < PaperId , string > = {
24+ overview : 'Your Angular Agent Readiness Guide' ,
25+ angular : 'Your Enterprise Guide to Agent Streaming' ,
26+ render : 'Your Enterprise Guide to Generative UI' ,
27+ chat : 'Your Enterprise Guide to Agent Chat Interfaces' ,
28+ } ;
29+
830export async function POST ( req : NextRequest ) {
931 let body : { name ?: string ; email ?: string ; paper ?: string } ;
1032 try {
@@ -13,18 +35,40 @@ export async function POST(req: NextRequest) {
1335 return NextResponse . json ( { error : 'Invalid JSON' } , { status : 400 } ) ;
1436 }
1537
16- const { name = '' , email = '' , paper = 'overview' } = body ;
38+ const name = ( body . name || '' ) . trim ( ) . slice ( 0 , 200 ) ;
39+ const email = ( body . email || '' ) . trim ( ) . slice ( 0 , 320 ) ;
40+ const paper = ( VALID_PAPERS . includes ( body . paper as PaperId ) ? body . paper : 'overview' ) as PaperId ;
41+
1742 if ( ! email || ! email . includes ( '@' ) ) {
1843 return NextResponse . json ( { error : 'Valid email required' } , { status : 400 } ) ;
1944 }
2045
21- const entry = JSON . stringify ( { name : name . trim ( ) , email : email . trim ( ) , paper : paper . trim ( ) , ts : new Date ( ) . toISOString ( ) } ) + '\n' ;
46+ // Persist signup to NDJSON (always, even if email fails)
47+ const entry = JSON . stringify ( { name, email, paper, ts : new Date ( ) . toISOString ( ) } ) + '\n' ;
2248 try {
2349 fs . mkdirSync ( path . dirname ( SIGNUPS_FILE ) , { recursive : true } ) ;
2450 fs . appendFileSync ( SIGNUPS_FILE , entry , 'utf8' ) ;
2551 } catch ( err ) {
2652 console . error ( 'Failed to write signup:' , err ) ;
27- return NextResponse . json ( { error : 'Internal error' } , { status : 500 } ) ;
53+ }
54+
55+ // Send download confirmation + schedule drip + sync contacts (best-effort)
56+ try {
57+ const downloadHtml = DOWNLOAD_EMAILS [ paper ] ( name || undefined ) ;
58+ await Promise . all ( [
59+ sendEmail ( {
60+ from : FROM ,
61+ to : email ,
62+ subject : DOWNLOAD_SUBJECTS [ paper ] ,
63+ html : downloadHtml ,
64+ } ) ,
65+ scheduleWhitepaperDrip ( email , paper ) ,
66+ addToAudience ( email , name || undefined ) ,
67+ loopsUpsertContact ( { email, firstName : name || undefined , source : `whitepaper-${ paper } ` } ) ,
68+ loopsSendEvent ( { email, eventName : 'whitepaper_downloaded' , properties : { paper } } ) ,
69+ ] ) ;
70+ } catch ( err ) {
71+ console . error ( '[whitepaper-signup] email pipeline failed:' , err ) ;
2872 }
2973
3074 return NextResponse . json ( { ok : true } ) ;
0 commit comments