PORTAL / LIBRARY / auto-growth-audit

[ 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.

Download the skill file (.md)

Placeholders like 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

Step-by-Step

Step 1 — Preflight checks

Before invoking, verify:

  1. .env has all required keys (see Environment section below)
  2. PM2 services are running: pm2 list should show audit-webhook and audit-worker
  3. Drive folder Klijenti/_Lead Audits/ exists; ID is in <id>
  4. GHL custom field audit_pdf_url is set up (URL type) and <id> is in env
  5. GHL workflow Audit Rasta Email Send exists 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.

#StageHelper
1Resolve website URL (form field, fallback to Firecrawl search of naziv_firme + grad)lib/firecrawl.mjs
2Site screenshots (desktop full-page + mobile + 3 viewport slices) + Firecrawl markdownlib/pdf_render.mjs:screenshotUrl, lib/firecrawl.mjs:scrapeWithMeta
3Vision audit: 5 strengths, 5 weaknesses, conversion verdictinline Anthropic call
4Meta Ad Library spy with derived keywordsACME Agency/scripts/spy_ad_library.mjs
5Google Ads Transparency + SERP captureACME Agency/scripts/<id>.mjs
6Competitor LP screenshots + verdict (top 2-3 from stages 4-5)lib/pdf_render.mjs:screenshotUrl
7Offer concept (Hormozi 5 attraction offers + prospect data).claude/context/hormozi/books/100m-money-models.md
8LP mockup (Krea single image)ACME Agency/scripts/krea_create_images.mjs
92-3 ad creative examples (static-ad-generator Mode B)ACME Agency/scripts/static_ads_generate.mjs
1030-day roadmap (Paradox playbook, budget mapped from form)inline template
11PDF compose + render (HTML template → PDF)lib/audit_pdf_template.mjs, lib/pdf_render.mjs:renderPdf
12Deliver: Drive upload → GHL contact upsert + tag → Slack #your-channellib/google_drive.mjs, lib/ghl.mjs:<id>, lib/slack.mjs

Step 4 — Output verification

Each successful run produces:

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):

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)

  1. Custom field: Create audit_pdf_url (URL type) on Contact. Copy field ID → <id>.
  2. Workflow: Create Audit Rasta Email Send with:
  1. PIT: Generate Private Integration Token for your-domain.example GHL location → GHL_PARADOX_API_KEY.
  2. 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

FailureBehaviorRecovery
Bad website URL, Firecrawl failsStage 1 falls back to company-name search; if that also fails, sets website_resolved: false and PDF skips site sectionsManual: edit queue file, set correct website_url, move from failed/ back to root
/spy timeoutSkips Stage 4, PDF says "Meta data unavailable"Re-run with --stages 4 only
Krea quotaSkips Stages 8 + 9Wait for quota reset, re-run with --stages 8,9
GHL upsert failsPDF still uploads to Drive + Slack notifies, but no email goes outManual: 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

Key files

FilePurpose
shared/audit_webhook.mjsHTTP receiver (port 3002), HMAC, enqueue
ACME Agency/scripts/<id>.mjsPM2 worker, polls queue, claims jobs
ACME Agency/scripts/auto_growth_audit.mjs12-stage pipeline orchestrator
ACME Agency/scripts/<id>.mjsGoogle SERP + Transparency Center scraper
ACME Agency/scripts/lib/audit_keywords.mjsNiche detection + keyword derivation
ACME Agency/scripts/lib/audit_pdf_template.mjsHTML 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.mdOffer-crafting reference