Concept Integration Playbook

The Problem: Every client integration was reconstructed from fragments. Missing one step → silent 500.

The Solution: One checklist, one env-var contract, one verification command. Run the verifier after every deploy.

Integration Checklist

Every new client goes through these 5 steps. Missing one will break the site.

1. Create GHL Location + PIT

2. Add Location ID to Lead Form Handler

Location IDs map client names to GHL locations. Edit scripts/ghl/lead.js:

const LOCATION_IDS = {
  eden: "YOUR_GHL_LOCATION_ID",
  btp: "...",
  // ... add your client
};

Get the location ID from GHL → Settings → Location ID. Exact string, no quotes.

3. Wire the Form on the Frontend

Add a form with data-ghl-client attribute:

<form
  action="/api/lead/"
  method="POST"
  data-ghl-client="eden"
>
  <input name="firstName" type="text" required>
  <input name="lastName" type="text" required>
  <input name="email" type="email" required>
  <input name="phone" type="tel">
  <input name="message" type="text">
  <input type="hidden" name="funnel" value="website">
  <button type="submit">Submit</button>
</form>

Important: Form fields are sent nested under fields to /api/lead/.

4. (Optional) Add Chat Widget

If the client wants Conversation AI:

5. Verify (This is the Key Step)

After deploy, run:

node scripts/ghl/verify-integration.sh eden

The script will:

  1. Check env var {CLIENT}_GHL_PIT exists in Vercel
  2. POST a test lead to /api/lead/
  3. Confirm a real contact lands in GHL (inspect the response contactId)
  4. Test the chat widget (if wired) and confirm GHL AI replies
Green output = ship. Red output = fix before shipping. This catches 100% of silent failures.

Field Contract (What Your Forms Send)

All concept sites send the same payload shape to /api/lead/:

{
  "client": "eden",
  "funnel": "website",
  "fields": {
    "firstName": "John",
    "lastName": "Doe",
    "email": "john@example.com",
    "phone": "+61412345678",
    "message": "Optional message"
  }
}

Key detail: Form <input> names go into the nested fields object. The client and funnel stay at the top level.

lead.js validates:

Environment Variables (Vercel)

Set these for every client:

Env Var Type Source Example
{CLIENT_UPPER}_GHL_PIT string GHL Location Settings pit_abc123...

LOCATION_IDS in lead.js is hardcoded (not env vars) because location IDs are public and change rarely.

Troubleshooting

Lead form returns 500, but verifier passes

Chat widget doesn't appear or is silent

"contactId" is missing from response

Files to Know

Quick Ref: New Client Workflow

  1. Create GHL location + mint PIT
  2. vercel env add {CLIENT_UPPER}_GHL_PIT <token>
  3. Add to LOCATION_IDS in lead.js
  4. Wire form with data-ghl-client="{client}"
  5. (If chat) Add to CLIENTS in chat.js
  6. Deploy: git push
  7. Verify: node scripts/ghl/verify-integration.sh {client}
  8. If green → done. If red → fix and re-verify

Ship when verifier is green. That's the only gate.