[ PAID MEDIA ]
/auto-growth-audit
When a prospect submits the form at `your-domain.example/audit`, an automated pipeline produces a personalized 12-15 page PDF audit and emails it via GHL.
ACME Agency, <id> and you@example.com mark values that are per-agency — your install fills them with YOUR clients and accounts. If a section references a helper script you don't have yet, it ships with that workflow's install./auto-growth-audit — Personalized Growth Audit Generator
Purpose
When a prospect submits the form at your-domain.example/audit, an automated pipeline produces a personalized 12-15 page PDF audit and emails it via GHL. Both fit and not-fit prospects receive it. Fit gets it 24h pre-call as a teammate's prep + their teaser. Not-fit gets it as the standalone deliverable.
Manual invocation exists for: testing changes to any pipeline stage, retrying failed jobs, generating audits for warm referrals not coming through the form.
Architecture
Lovable form → GHL → POST https://bridge.your-domain.example/audit
↓ (HMAC-validated by shared/audit_webhook.mjs on port 3002)
↓
queue file: ACME Agency/queue/audit/<uuid>.json
↓
worker (PM2): ACME Agency/scripts/<id>.mjs
↓
pipeline: ACME Agency/scripts/auto_growth_audit.mjs
↓ 12 stages, ~3-5 min wall time
↓
PDF → Drive → GHL contact tag (audit-pdf-ready) → email workflow fires
↓
Slack #your-channel notification
Triggers
- Automatic (preferred): form submission → webhook → queue → worker (no human action)
- Manual via skill:
/auto-growth-audit --job ACME Agency/queue/audit/<uuid>.json(replay a queued job) - Manual standalone audit:
/auto-growth-audit --url https://example.hr --company "Example" --lokacija "Zagreb i okolica"(no GHL delivery, just PDF to Drive) - Lead-magnet mode (URL-only, for setters & salespeople):
/auto-growth-audit --url https://example.hr --company "Example" --mode lead-magnet --no-deliver. Skips form-data sections (snapshot + Trenutno stanje), keeps everything else (vision audit, Meta + Google competitor analysis, competitor LPs, offer, ad examples, roadmap with generic-budget benchmarks). Filename gets alead-magnet_prefix in Drive; Slack post header reads(LEAD MAGNET)instead of FIT/NOT_FIT. Setters trigger this via Slack DM to claude-bot — natural-language patterns like "napravi quick audit za https://example.hr Firma X", "kreiraj audit za sales call X", "make a lead-magnet audit for X" route to this mode.
Step-by-Step
Step 1 — Preflight checks
Before invoking, verify:
.envhas all required keys (see Environment section below)- PM2 services are running:
pm2 listshould showaudit-webhookandaudit-worker - Drive folder
Klijenti/_Lead Audits/exists; ID is in<id> - GHL custom field
audit_pdf_urlis set up (URL type) and<id>is in env - GHL workflow
Audit Rasta Email Sendexists with trigger "tag added: audit-pdf-ready"
Step 2 — Identify the input
From queue file:
node ACME Agency/scripts/auto_growth_audit.mjs \
--job ACME Agency/queue/audit/<uuid>.json
Standalone (no GHL delivery):
node ACME Agency/scripts/auto_growth_audit.mjs \
--url https://ACME Agency.hr \
--company "ACME Agency" \
--lokacija "Zagreb i okolica" \
--email you@example.com \
--no-deliver
Stage subset (testing):
node ACME Agency/scripts/auto_growth_audit.mjs \
--job <file> \
--stages 1,2,3 \
--no-deliver
Step 3 — Pipeline stages
The orchestrator runs 12 stages. Each is fail-soft: if a stage errors, log and continue. PDF composes with whatever succeeded. >2 failures sets a manual_ACME Agencyw flag on the Slack notification.
| # | Stage | Helper |
|---|---|---|
| 1 | Resolve website URL (form field, fallback to Firecrawl search of naziv_firme + grad) | lib/firecrawl.mjs |
| 2 | Site screenshots (desktop full-page + mobile + 3 viewport slices) + Firecrawl markdown | lib/pdf_render.mjs:screenshotUrl, lib/firecrawl.mjs:scrapeWithMeta |
| 3 | Vision audit: 5 strengths, 5 weaknesses, conversion verdict | inline Anthropic call |
| 4 | Meta Ad Library spy with derived keywords | ACME Agency/scripts/spy_ad_library.mjs |
| 5 | Google Ads Transparency + SERP capture | ACME Agency/scripts/<id>.mjs |
| 6 | Competitor LP screenshots + verdict (top 2-3 from stages 4-5) | lib/pdf_render.mjs:screenshotUrl |
| 7 | Offer concept (Hormozi 5 attraction offers + prospect data) | .claude/context/hormozi/books/100m-money-models.md |
| 8 | LP mockup (Krea single image) | ACME Agency/scripts/krea_create_images.mjs |
| 9 | 2-3 ad creative examples (static-ad-generator Mode B) | ACME Agency/scripts/static_ads_generate.mjs |
| 10 | 30-day roadmap (Paradox playbook, budget mapped from form) | inline template |
| 11 | PDF compose + render (HTML template → PDF) | lib/audit_pdf_template.mjs, lib/pdf_render.mjs:renderPdf |
| 12 | Deliver: Drive upload → GHL contact upsert + tag → Slack #your-channel | lib/google_drive.mjs, lib/ghl.mjs:<id>, lib/slack.mjs |
Step 4 — Output verification
Each successful run produces:
- PDF at
Klijenti/_Lead Audits/<YYYY-MM-DD>_<company>_<jobId>.pdfon Drive - Slack post in
#your-channelwith: prospect name, qualification flag, processing time, Drive link, list of any failed stages - GHL contact: created or updated,
audit_pdf_urlpopulated, taggedaudit-pdf-ready
If a stage fails: queue file moves to ACME Agency/queue/audit/failed/ with an _error.txt companion file. Slack post flags manual_ACME Agencyw: true.
Environment variables
Required in .env:
AUDIT_WEBHOOK_PORT=3002
AUDIT_WEBHOOK_SECRET=<32-char hex secret, used for HMAC>
<id>=<your-domain.example location ID>
GHL_PARADOX_API_KEY=<PIT for your-domain.example GHL location, OR env-var name pointing to it>
<id>=<custom field ID for audit_pdf_url>
<id>=<channel ID for #your-channel>
<id>=<Drive folder ID for Klijenti/_Lead Audits/>
Already present (reused):
ANTHROPIC_API_KEY— vision + keyword derivation + offer craftingFIRECRAWL_API_KEY— site scrapeKREA_API_KEY— LP mockup + ad examples<id>— Drive uploadSLACK_BOT_TOKEN— notifications
PM2 setup
# Webhook receiver (port 3002)
pm2 start "C:\Users\faris\agency-os\shared\audit_webhook.mjs" --name audit-webhook --update-env
# Queue worker (polls ACME Agency/queue/audit/)
pm2 start "C:\Users\faris\agency-os\ACME Agency\scripts\<id>.mjs" --name audit-worker --update-env
# Save state so they auto-restart on reboot
pm2 save
Verify:
pm2 list
curl http://localhost:3002/health
# Expected: {"ok":true,"service":"audit_webhook","port":3002}
GHL setup (manual, one-time, outside code)
- Custom field: Create
audit_pdf_url(URL type) on Contact. Copy field ID →<id>. - Workflow: Create
Audit Rasta Email Sendwith:
- Trigger: Contact tag added →
audit-pdf-ready - Action 1: Send email (HTML template with
{{contact.audit_pdf_url}}linked button) - Action 2: Add tag
audit-sent
- PIT: Generate Private Integration Token for your-domain.example GHL location →
GHL_PARADOX_API_KEY. - Drive folder: Create
Klijenti/_Lead Audits/. Copy folder ID →<id>.
Lovable LP integration
The existing form at /audit POSTs to a GHL webhook. Configure that GHL webhook to forward to:
POST https://bridge.your-domain.example/audit
Headers:
X-Audit-Signature: sha256=<HMAC-SHA256 of body using AUDIT_WEBHOOK_SECRET>
Content-Type: application/json
Body: <form payload as-is>
Configure in GHL workflow's HTTP Request step. The HMAC can be computed via a workflow JS step or a small intermediate Cloudflare Worker if GHL doesn't support it natively.
Local testing without GHL:
# Generate test signature
SIG=$(echo -n '{"email":"you@example.com","qualification":"fit",...}' | openssl dgst -sha256 -hmac "$AUDIT_WEBHOOK_SECRET" | sed 's/^.* /sha256=/')
curl -X POST http://localhost:3002/audit \
-H "X-Audit-Signature: $SIG" \
-H "Content-Type: application/json" \
-d '{...}'
Failure modes & recovery
| Failure | Behavior | Recovery |
|---|---|---|
| Bad website URL, Firecrawl fails | Stage 1 falls back to company-name search; if that also fails, sets website_resolved: false and PDF skips site sections | Manual: edit queue file, set correct website_url, move from failed/ back to root |
| /spy timeout | Skips Stage 4, PDF says "Meta data unavailable" | Re-run with --stages 4 only |
| Krea quota | Skips Stages 8 + 9 | Wait for quota reset, re-run with --stages 8,9 |
| GHL upsert fails | PDF still uploads to Drive + Slack notifies, but no email goes out | Manual: pull contact id from Slack, run /auto-growth-audit --redeliver <jobId> |
Any 2+ stage failures → Slack #your-channel flags manual_ACME Agencyw: true and Faris/a teammate handles personally.
Output examples
Successful run Slack post (Bosnian):
*Audit Rasta gotov — ACME Agency (Zagreb)*
• Status: poslat preko GHL workflow-a
• Profil: fit (10-25 upita/mj, 1.5-5K€ ad spend)
• Vrijeme: 4m 12s
• PDF: <Drive link>
• Stages: 12/12 OK
Manual ACME Agencyw Slack post:
*Audit Rasta — ACME Agencyw needed — XYZ Salon*
• Status: PDF gotov, NIJE poslat (3 stages failed)
• Failed: Stage 4 (Meta spy timeout), Stage 5 (Google block), Stage 8 (Krea quota)
• PDF: <Drive link>
• Job: ACME Agency/queue/audit/failed/<uuid>.json
Rules
- Language: PDF + Slack always Bosnian (BCS), unless prospect lokacija is "Njemačka i Austrija" (German PDF in v2)
- No em-dashes: applies to all generated copy in the PDF
- Diacritics preserved: š, ć, č, ž, đ throughout
- Fail-soft: never crash the whole pipeline for one bad stage
- Idempotent retry: re-running a job file is safe — PDF gets a fresh timestamp, GHL upsert is no-op if contact already exists
- No auto-send when AUDIT_AUTO_SEND=false: Stage 12 still uploads to Drive + Slack but skips GHL tag step. Used during the first 5-10 ACME Agencyw cycles before flipping to true
Key files
| File | Purpose |
|---|---|
shared/audit_webhook.mjs | HTTP receiver (port 3002), HMAC, enqueue |
ACME Agency/scripts/<id>.mjs | PM2 worker, polls queue, claims jobs |
ACME Agency/scripts/auto_growth_audit.mjs | 12-stage pipeline orchestrator |
ACME Agency/scripts/<id>.mjs | Google SERP + Transparency Center scraper |
ACME Agency/scripts/lib/audit_keywords.mjs | Niche detection + keyword derivation |
ACME Agency/scripts/lib/audit_pdf_template.mjs | HTML template for the PDF |
ACME Agency/scripts/lib/ghl.mjs (extended) | upsertContact, addContactTags, <id> |
ACME Agency/queue/audit/ | Queue dir with processing/, done/, failed/ subdirs |
.claude/context/hormozi/books/100m-money-models.md | Offer-crafting reference |