Sprint 1 Readme
Sprint 1 — Deployable Files · AI Opportunity Audit
Section titled “Sprint 1 — Deployable Files · AI Opportunity Audit”This Sprint 1 package is everything you need to wire the AI Opportunity Audit fulfillment pipeline end-to-end: intake → Claude analysis → branded deliverables → delivery → referral loop. All GHL-free. ~$0/mo recurring beyond your existing VPS.
Target markets: AU, EU, UAE, higher-tier APAC. Price in USD. Website: dwadlane.com.
📦 What’s in this folder
Section titled “📦 What’s in this folder”| File | What it is | Import target |
|---|---|---|
supabase_schema.sql |
10 tables + RLS + triggers + engagement-code generator | Supabase SQL Editor |
n8n_workflow.json |
29-node fulfillment pipeline (importable) | n8n → Import from File |
claude_prompt_library.md |
5 master Claude prompts + API key injection docs | Reference (copy system prompts into n8n nodes) |
email_swipe_file.md |
5-part nurture sequence, Resend-ready | Reference (copy into n8n Resend nodes) |
notion_build_log_template.md |
Build-log database + page template | Notion (manual setup) |
README.md |
This file — wiring + setup sequence | — |
🏗️ Architecture (data flow)
Section titled “🏗️ Architecture (data flow)”Tally (intake) ──webhook──► n8n ──► Supabase (intake_responses) + automation_state.intake_completed + Resend ack email → cal.com booking
[you run interviews, transcripts land in interview_notes]
Admin trigger ──webhook──► n8n "Run Analysis" ──► Supabase: fetch engagement + intake + interviews ──► Claude Sonnet ×4: scoring → opportunities → roadmap → ROI ──► Claude Haiku ×1: Loom script draft ──► Supabase: analysis_drafts (status: in_review) + notify you
[you review + flip status → approved]
Approval webhook ──► n8n "Generate Deliverables" ──► Gotenberg: branded PDF ─┐ ──► Placid: share graphic ├─► Supabase: deliverables row ─┘ + automation_state.deliverables_ready ──► Resend: delivery email (PDF + portal + Loom + debrief booking)
[you record Loom → webhook updates deliverables.loom_url]
──► Supabase: create referral row (15% / 12-mo tail) ──► Resend: referral email (24h delay) ──► Notion: auto-create build-log pageSource of truth: Supabase. n8n reads/writes via the PostgREST API using the service role key (bypasses RLS — n8n only). The Next.js client portal uses Supabase Auth + the anon/publishable key with RLS policies scoping reads to the client’s own engagement via portal_magic_token.
🔑 Credentials & Environment Variables
Section titled “🔑 Credentials & Environment Variables”Create these accounts/keys first
Section titled “Create these accounts/keys first”| Service | What you need | Where |
|---|---|---|
| Supabase | Project URL, service role key, anon key | console.supabase.com → Settings → API |
| Anthropic | API key (sk-ant-...) |
console.anthropic.com/settings/keys |
| Resend | API key + verified domain (dwadlane.com) | resend.com/domains |
| Placid | API key + template ID (square + landscape) | placid.app |
| Notion | Internal integration token + database ID | notion.so/my-integrations |
| Gotenberg | (self-hosted, no key) | Docker on VPS — see below |
| Tally | Form ID + webhook URL | tally.so |
| cal.com | Booking page URL + (optional) API key | cal.com |
| Stripe | Secret key (checkout) | dashboard.stripe.com → API keys |
| DocuSeal | API key (e-sign) | docusign.com → developers |
n8n environment variables
Section titled “n8n environment variables”Set these in your n8n instance (Docker env file or n8n Settings → Variables):
# SupabaseSUPABASE_URL=https://YOURPROJ.supabase.coSUPABASE_SERVICE_ROLE_KEY=eyJ... # n8n only — full access, bypasses RLS# (anon key is used by the portal frontend, not n8n)
# Anthropic (Claude) — model names are parameterized so you can bump versionsCLAUDE_SONNET_MODEL=claude-sonnet-4-5-20250929CLAUDE_HAIKU_MODEL=claude-haiku-4-5-20251022
# ResendRESEND_API_KEY=re_...MAIL_FROM=dwad@dwadlane.com
# PlacidPLACID_API_KEY=...PLACID_TEMPLATE_ID=...
# NotionNOTION_API_KEY=ntn_...NOTION_BUILDLOG_DB_ID=...
# Gotenberg (self-hosted on VPS, localhost only)GOTENBERG_URL=http://localhost:3000
# AppSITE_URL=https://dwadlane.comPORTAL_BASE_URL=https://dwadlane.com/portalCALCOM_URL=https://cal.com/dwadlane/auditINTERNAL_NOTIFY_EMAIL=dwad.lane@gmail.comREFERRAL_PCT=15n8n credentials (named, reusable) — create in n8n → Credentials
Section titled “n8n credentials (named, reusable) — create in n8n → Credentials”| Credential name | Type | Used by |
|---|---|---|
anthropicApi |
Header Auth (x-api-key → your Anthropic key) |
All 5 Claude nodes |
resendApi |
Header Auth (Authorization → Bearer re_...) |
All Resend nodes |
placidApi |
Header Auth (Authorization → Bearer ...) |
Placid node |
Why Header Auth instead of pasting keys in nodes? Each Claude/Resend/Placid node references the credential by name. Rotate a key once → all nodes update. The key never appears in the workflow JSON or this README.
📥 Where to inject the Claude API key
Section titled “📥 Where to inject the Claude API key”This is the single most important wiring step. Three layers:
- Get the key from https://console.anthropic.com/settings/keys.
- Store it as an n8n credential named
anthropicApi, type Header Auth:- Name:
anthropicApi - Header name:
x-api-key - Value:
sk-ant-api03-...(paste your key here)
- Name:
- Each of the 5 Claude nodes in
n8n_workflow.jsonis pre-configured to use it:When you open the node after import, select the"authentication": "genericCredentialType","genericAuthType": "httpHeaderAuth"anthropicApicredential from the dropdown. Done.
Never do:
- Paste the key into the
jsonBodyof a node - Paste the key into
claude_prompt_library.md - Commit the key to GitHub
Full details + cost guardrails in claude_prompt_library.md → section “API KEY INJECTION — WHERE & HOW”.
🔗 How Supabase tables hook into n8n logic
Section titled “🔗 How Supabase tables hook into n8n logic”n8n talks to Supabase exclusively through the PostgREST API (/rest/v1/...) + Storage API (/storage/v1/...), authenticated with the service role key. Here’s the mapping of workflow nodes → tables:
| Workflow node group | Supabase table | Operation |
|---|---|---|
| Tally intake webhook → “Supabase: Insert Intake” | intake_responses |
INSERT |
| “Supabase: Mark Intake Done” | automation_state |
PATCH (intake_completed=true) |
| “Run Analysis” → fetch nodes | engagements, intake_responses, interview_notes |
SELECT |
| “Claude: Dimension Scoring” → “Assemble Draft” → “Store Analysis” | analysis_drafts |
INSERT (all 5 prompt outputs in one row) |
| “Mark Analysis Ready” | automation_state |
PATCH (analysis_ready=true) |
| Approval webhook → “Fetch Approved Analysis” | analysis_drafts |
SELECT (status=approved) |
| “Gotenberg Render PDF” → “Supabase Storage: Upload PDF” | Storage bucket deliverables |
PUT binary |
| “Store Deliverables” | deliverables |
INSERT (pdf_url, graphic_url, portal_url, magic_token) |
| “Mark Deliverables Ready” / “Mark Delivered” | automation_state |
PATCH |
| “Create Referral” | referrals |
INSERT (ref_code, 15%, 12-mo tail) |
| Delivery / referral / nurture emails | email_log |
INSERT (for nurture analytics) |
Key relationships (foreign keys):
engagements.contact_id→contacts.idintake_responses.engagement_id→engagements.idinterview_notes.engagement_id→engagements.idanalysis_drafts.engagement_id→engagements.iddeliverables.engagement_id→engagements.idautomation_state.engagement_id→engagements.id(1:1, PK)referrals.referrer_contact_id/referred_contact_id→contacts.id
Engagement code (ENG-2026-014) is auto-generated by the generate_engagement_code() trigger on insert — you never set it manually.
RLS: enabled on contacts, engagements, deliverables, automation_state. The service role key (n8n) bypasses RLS entirely. The portal uses anon key + the portal_read_own_deliverables / portal_read_own_state policies, scoped by portal_magic_token.
🚀 Setup Sequence (do in this order)
Section titled “🚀 Setup Sequence (do in this order)”1. Database (15 min)
Section titled “1. Database (15 min)”- Create a Supabase project (free tier is fine to start).
- SQL Editor → paste
supabase_schema.sql→ Run. - Settings → API → copy Project URL, service role key, anon key.
- Storage → create a public bucket named
deliverables(or private + signed URLs — your call).
2. n8n host (30 min)
Section titled “2. n8n host (30 min)”- On your Hostinger VPS (or any Docker host), run n8n + Gotenberg via Docker Compose. Minimal:
services:n8n:image: n8nio/n8nports: ["5678:5678"]environment:- N8N_HOST=n8n.dwadlane.com- WEBHOOK_URL=https://n8n.dwadlane.com- GENERIC_TIMEZONE=Asia/Manila# + all the env vars from the section aboveenv_file: .envvolumes: ["n8n_data:/home/node/.n8n"]gotenberg:image: gotenberg/gotenberg:8ports: ["3000:3000"] # localhost only — n8n calls internallyvolumes: { n8n_data: {} }
- Put Cloudflare in front (DNS → your VPS, proxy on, Full SSL).
- Open n8n → Settings → Variables → add every env var listed above.
- Credentials → create
anthropicApi,resendApi,placidApi(Header Auth types). - Import workflow:
n8n_workflow.json→ open each Claude/Resend/Placid node → select the matching credential from the dropdown.
3. Forms & scheduling (20 min)
Section titled “3. Forms & scheduling (20 min)”- Tally → build the deep intake form (see Asset 2 / Asset 9 for the question set). Add hidden field
engagement_id. Integrate → Webhook → your n8n Tally webhook URL. - cal.com → set up an “AI Audit Scoping” event type (30 min) + “Stakeholder Interview” (60 min). Copy the booking URLs into env vars.
- (Optional) DocuSeal → upload the SOW + NDA templates (Asset 4). Webhook on signature-complete → n8n →
automation_state.signed_received=true.
4. Payments (15 min)
Section titled “4. Payments (15 min)”- Stripe → create 3 Payment Links for the tiers ($1.5K / $3K / $5K).
- On checkout success, Stripe webhook → n8n → INSERT
engagements(status: won) + INSERTautomation_staterow + Resend “You’re in” email with intake + booking links.
5. Email (15 min)
Section titled “5. Email (15 min)”- Resend → verify dwadlane.com domain (add DNS records).
- Copy the 5 nurture emails from
email_swipe_file.mdinto 5 n8n Resend nodes (or a Resend Audience sequence — see the file’s final section for the lighter path).
6. Deliverable rendering (1 hr)
Section titled “6. Deliverable rendering (1 hr)”- Gotenberg is already running on the VPS. Build an HTML template for the branded report (see Asset 5 for the design spec). n8n’s “Gotenberg: Render PDF” node POSTs the HTML + the approved-analysis JSON.
- Placid → build 2 templates: square 1080×1080 + landscape 1200×627, both with
{score},{client_name},{radar_data}layers. Copy the template ID intoPLACID_TEMPLATE_ID.
7. Client portal (optional for Sprint 1, build in Sprint 2)
Section titled “7. Client portal (optional for Sprint 1, build in Sprint 2)”- Next.js on Vercel, dwadlane.com/portal/[engagement_id]?token=…
- Reads
automation_state+deliverablesvia anon key + magic token. Renders the radar chart, opportunity heatmap, roadmap tiles. - Until then, the delivery email links directly to the PDF + Loom — fully functional.
8. Notion build-log (15 min)
Section titled “8. Notion build-log (15 min)”- Create the “AI Audit Build Logs” database with the properties in
notion_build_log_template.mdSection A. - Set the page template (Section B) as the default.
- Notion → create internal integration → copy token into
NOTION_API_KEY+ database ID intoNOTION_BUILDLOG_DB_ID. - After “Supabase: Mark Delivered” in n8n, add a Notion node that auto-creates the build-log row (see Section D of the template).
✅ Smoke test (run before any paying client)
Section titled “✅ Smoke test (run before any paying client)”- Self-audit first. Run the full pipeline on your own business (dwadlane.com / your LMS data). You’ll find every broken node before a client does.
- Walk through: Tally intake →
intake_responsesrow exists →automation_state.intake_completed=true→ ack email arrived → manually trigger Run Analysis → 5 Claude calls succeed →analysis_draftsrow exists with all 5 fields → review email arrived → flip status to approved → PDF renders + graphic generates +deliverablesrow exists → delivery email arrives → referral row created → build-log page appears in Notion. - Verify the JSON parse step works (Claude sometimes wraps output in fences — the Code node in
claude_prompt_library.mdhandles this).
🧭 What’s NOT in Sprint 1 (deferred to Sprint 2)
Section titled “🧭 What’s NOT in Sprint 1 (deferred to Sprint 2)”- Client portal UI (radar/heatmap/roadmap tiles) — functional via email links until then
- Metabase benchmark analytics dashboard
- Automated Wise payout for referral commissions (manual for now)
- DocuSeal auto-signing flow wired into n8n (templates ready in Asset 4)
- The scorecard lead-magnet pipeline (ScoreApp → nurture) — spec is in Asset 3 Pipeline 1; the nurture emails you need are already in this package
🆘 Troubleshooting
Section titled “🆘 Troubleshooting”| Symptom | Fix |
|---|---|
| Claude node returns 401 | anthropicApi credential not selected on the node, or wrong header name (x-api-key) |
| Claude output fails JSON.parse | Add the Code node from claude_prompt_library.md (strips markdown fences) |
| Supabase 401 on insert | Using anon key instead of service role key in the node headers |
| Tally webhook not firing | Form must be published; webhook URL must be the n8n production webhook URL (not test) |
| Gotenberg PDF blank | HTML template not attached as files multipart field; check the node’s inputDataFieldName |
| Placid 401 | placidApi credential uses Authorization: Bearer ..., not x-api-key |
| Engagement code not generating | Trigger trg_engagement_code only fires on INSERT when engagement_code IS NULL — ensure you don’t set it manually |
📍 Related assets (in workspace root, not this folder)
Section titled “📍 Related assets (in workspace root, not this folder)”Asset_3_n8n_Pipeline_Spec.md— the full spec this workflow implementsAsset_2_ScoreApp_Questionnaire.md— the scorecard question set + scoring logicAsset_5_PDF_Deliverable_Template.md— the branded report design specAsset_9_Execution_Playbook.md— the cold-beginner execution guideAsset_12_Master_Playbook_v2.md— the integrated strategy docAsset_13_Productization_roadmap.md— the $5K “how to sell AI audits” product plan
Sprint 1 status: 6/6 files complete. Ready for import + smoke test.