A Developer's Guide
THE GOHIGHLEVEL
API COOKBOOK
50+ Recipes for Building Automations with the GHL API
From Custom Webhooks to Production AI Systems
Julius "Jenius with a J" Guevarra
Founder, BuildAI School  ·  CTO/Co-Founder, SmartChat
v1.0 | March 2026
BuildAI School  |  SmartChat

Table of Contents

How to Use This Cookbook 4

About the API Playground 5

Ch 1: Getting Started

  • 1.1 Get Your API Credentials 7
  • 1.2 Your First Contact Search 8
  • 1.3 Create Your First Contact 9
  • 1.4 Upsert — Create or Update Smartly 10
  • 1.5 Check for Duplicates 11

Ch 2: The Custom Webhook Pattern

  • 2.1 Auto-Tag Leads by Source 13
  • 2.2 Auto-Create Pipeline Opportunities 14
  • 2.3 Smart Auto-Booking System 15
  • 2.4 Pipeline Auto-Advance 16
  • 2.5 Complete Lead Lifecycle 17
  • Custom Webhook Quick Reference 18
  • Version Header Warning 19

Ch 3: GHL + n8n

  • 3.1 2-Way SMS with Custom Gateway 21
  • 3.2 AI Lead Scoring Engine 22
  • 3.3 Batch Contact Processing 23
  • 3.4 Voice AI Appointment Setter (Vapi) 24
  • 3.5 Voice AI with Retell 25
  • n8n HTTP Request Quick Setup 26–27

Ch 4: GHL + Make.com

  • 4.1 Auto Client Onboarding 29
  • 4.2 Cross-Object Associations 30
  • Make.com + GHL Setup Reference 31

Ch 5: GHL + Supabase

  • 5.1 Real-Time KPI Dashboard 33
  • 5.2 AI Inbox Classifier 34
  • 5.3 Token Management for Scale 35
  • Supabase Edge Function Template 36–37

Ch 6: Claude MCP + GHL

  • 6.1 Connect Claude to GHL 39
  • 6.2 Pipeline Health Audit 40
  • 6.3 Conversation Summarizer 41

Ch 7: Webhooks & Events

  • Webhook Events Reference 43
  • 7.1 Universal Event Router 44
  • Production Architecture Diagram 45

Ch 8: Endpoint Reference

  • Contacts Endpoints 47
  • Conversations Endpoints 48
  • Calendars & Opportunities 49
  • Locations & Users 50
  • OAuth & Advanced 51
  • Additional Endpoints 52
  • Scopes Reference 53

Ch 9: Troubleshooting

  • Common Errors 55
  • Rate Limits & Best Practices 56

Ch 10: What's Next

  • Resources & CTA 58
  • Back Cover 59

How to Use This Cookbook

Recipe Format

Every recipe in this book follows a consistent structure:

  • What You'll Build — The outcome in one sentence
  • Ingredients (Endpoints) — API endpoints and scopes needed
  • The Recipe (Steps) — Step-by-step implementation
  • How It Works — What's happening under the hood
  • Try It in the Playground — Test it live before building

Difficulty Levels

🟢 Beginner 🟡 Intermediate 🔴 Advanced

Tool Icons

🔧GHL Only (Custom Webhooks) + n8n (middleware) 🔮+ Make.com (visual automation) 🗄️+ Supabase (database) 🤖+ Voice AI / Claude MCP
Tip: This cookbook is designed to be used alongside the GHL API Playground at buildaischool.com/gohighlevel-api. Every recipe can be tested there before building it into a workflow.

About the API Playground

What It Is

A browser-based tool to test any GoHighLevel API endpoint. No Postman, no curl commands, no setup — just paste your token and go.

How to Connect

  1. Visit buildaischool.com/gohighlevel-api
  2. Enter your Private Integration Token
  3. Select your endpoint from the dropdown
  4. Click Send — see the response in real time

Features

  • Auto-fills required headers (Authorization, Version, Content-Type)
  • Shows formatted response data with syntax highlighting
  • Saves request history for quick re-testing
  • Supports all 413+ GHL API endpoints
  • Copy-paste friendly request/response panels
Key Insight: Every recipe in this cookbook can be tested in the Playground before you build it into a workflow. Test first, build second.
Chapter 1

GETTING STARTED

Pre-Flight Checks & Your First API Call
RECIPE 1.1

Get Your API Credentials

🟢 Beginner 🔧 GHL Only

What You'll Build

Set up your Private Integration Token and make your first authenticated API call.

Ingredients

Settings → Integrations → Private Integrations

The Recipe

  1. Navigate to Settings → Integrations
  2. Click "Create Integration"
  3. Name it (e.g., API Cookbook)
  4. Copy your Bearer Token
  5. Required headers for every API call:
Headers
Authorization: Bearer YOUR_TOKEN
Version: 2021-07-28
Content-Type: application/json

How It Works

The token authenticates every request you make. The Version header is required on EVERY request — GHL uses API versioning to manage breaking changes. Content-Type tells GHL you're sending JSON.

Try It: Open the API Playground, paste your token, hit "Test Connection."
RECIPE 1.2

Your First Contact Search

🟢 Beginner 🔧 GHL Only

What You'll Build

Search your contact database using the API.

Ingredients

POST /contacts/search · Scope: contacts.readonly

The Recipe

JSON
POST https://services.leadconnectorhq.com/contacts/search

// Headers
Authorization: Bearer YOUR_TOKEN
Version: 2021-07-28

// Body
{
  "locationId": "YOUR_LOCATION_ID",
  "page": 1,
  "pageLimit": 20,
  "filters": []
}

Response

Returns a contacts array with id, firstName, lastName, email, phone, and tags for each match.

How It Works

POST /contacts/search is the recommended way to find contacts. The old GET /contacts/ endpoint is deprecated. Always use POST search.

RECIPE 1.3

Create Your First Contact

🟢 Beginner 🔧 GHL Only

Ingredients

POST /contacts/ · Scope: contacts.write

The Recipe

JSON
POST https://services.leadconnectorhq.com/contacts/

{
  "locationId": "YOUR_LOCATION_ID",
  "firstName": "John",
  "lastName": "Doe",
  "email": "john@example.com",
  "phone": "+1234567890",
  "tags": ["api-created", "cookbook-test"],
  "source": "API Cookbook"
}

How It Works

locationId is always required — it tells GHL which sub-account to create the contact in. Tags help you track API-created contacts. The source field shows where the contact came from in the GHL UI.

Pro Tip: Always add a unique tag like "api-created" so you can easily find and manage contacts created via the API.
RECIPE 1.4

Upsert — Create or Update Smartly

🟢 Beginner 🔧 GHL Only

Ingredients

POST /contacts/upsert · Scope: contacts.write

The Recipe

JSON
POST https://services.leadconnectorhq.com/contacts/upsert

{
  "locationId": "YOUR_LOCATION_ID",
  "firstName": "John",
  "email": "john@example.com",
  "tags": ["returning-lead"]
}

How It Works

If a contact with that email exists, it updates the record. If not, it creates a new one. This is THE pattern for avoiding duplicates. It matches on email or phone.

Pro Tip: This single endpoint replaces the search-then-create-or-update dance. Use it. Always.
RECIPE 1.5

Check for Duplicates

🟢 Beginner 🔧 GHL Only

Ingredients

GET /contacts/search/duplicate · Scope: contacts.readonly

The Recipe

HTTP
GET /contacts/search/duplicate?locationId=XXX&email=john@example.com

How It Works

Returns matching contacts by email or phone. Use this before creating contacts if you need to handle duplicates with custom logic (e.g., merging fields, prompting the user).

When to use what:
• Need simple dedup? → Use upsert (Recipe 1.4)
• Need custom merge logic? → Use duplicate search first, then decide
Chapter 2

THE CUSTOM WEBHOOK PATTERN

GHL Calling Its Own API — No External Tools
RECIPE 2.1

Auto-Tag Leads by Source

🟢 Beginner 🔧 GHL Only Day 1 of 5-Day Challenge

What You'll Build

A workflow that searches for duplicate contacts and auto-tags them as "new" or "returning."

Ingredients

  • Workflow Trigger: Form Submitted
  • Custom Webhook → POST /contacts/search
  • If/Else condition
  • Add Tag action

The Recipe

  1. Create Workflow → Trigger: Form Submitted
  2. Add Action → Send Data → Custom Webhook
  3. Method: POST
  4. URL: https://services.leadconnectorhq.com/contacts/search
  5. Headers: Authorization (Bearer token), Version (2021-07-28), Content-Type (application/json)
  6. Body:
{
  "locationId": "{{location.id}}",
  "filters": [{
    "field": "email",
    "operator": "eq",
    "value": "{{contact.email}}"
  }]
}
  1. Add If/Else: Check response for matches
  2. Found → Tag "returning-lead" / Not Found → Tag "new-lead"
Cost: $0.01 per execution (100 free included).
Key Insight: This Custom Webhook just did what people pay $20/month for Zapier to do.
RECIPE 2.2

Auto-Create Pipeline Opportunities

🟢 Beginner 🔧 GHL Only

What You'll Build

Form submission automatically creates an opportunity in your sales pipeline.

Ingredients

  • Custom Webhook → GET /opportunities/pipelines (fetch pipeline & stage IDs)
  • Custom Webhook → POST /opportunities/ (create opportunity)

The Recipe

  1. Trigger: Form Submitted
  2. Webhook #1: GET /opportunities/pipelines?locationId={{location.id}}
    → Capture pipelineId and stageId from response
  3. Webhook #2: POST /opportunities/
{
  "pipelineId": "...",
  "locationId": "...",
  "name": "{{contact.name}} - New Deal",
  "status": "open",
  "contactId": "{{contact.id}}",
  "pipelineStageId": "...",
  "monetaryValue": 500
}
Key Insight: Two Custom Webhooks chained. First fetches data, second uses it. This is THE pattern.
RECIPE 2.3

Smart Auto-Booking System

🟡 Intermediate 🔧 GHL Only

What You'll Build

Check calendar availability and book an appointment — all inside GHL workflows.

Ingredients

  • Custom Webhook → GET /calendars/{id}/free-slots
  • Custom Webhook → POST /calendars/events/appointments
  • Custom Webhook → POST /conversations/messages (Version: 2021-04-15!)

The Recipe

  1. Trigger: Contact Tag Added = "book-now"
  2. Webhook #1: GET free-slots with startDate, endDate, timezone
  3. Extract first available slot from response
  4. Webhook #2: POST appointment with calendarId, contactId, startTime
  5. Webhook #3: POST SMS confirmation
⚠️ IMPORTANT: Change the Version header to 2021-04-15 for the Conversations endpoint! Using the wrong version is the #1 source of 400 errors.
Key Insight: Three API calls. Zero external tools. You just built what Calendly charges $12/month for.
RECIPE 2.4

Pipeline Auto-Advance on Appointment

🟡 Intermediate 🔧 GHL Only

What You'll Build

When an appointment completes, automatically advance the contact's pipeline stage.

Ingredients

  • Trigger: Appointment Status Changed
  • Custom Webhook → GET /opportunities/search
  • Custom Webhook → PUT /opportunities/{id}
  • Custom Webhook → POST /contacts/{id}/notes

The Recipe

  1. Trigger: Appointment Status = "showed"
  2. Webhook #1: Search opportunities by contactId, status=open
  3. If/Else: opportunity found?
  4. Webhook #2: PUT opportunity with new pipelineStageId
  5. Webhook #3: POST note logging the stage change with timestamp
Key Insight: Your pipeline moves without you touching it. Appointments drive pipeline progression automatically.
RECIPE 2.5 — CAPSTONE

The Complete Lead Lifecycle

🟡 Intermediate 🔧 GHL Only

What You'll Build

A multi-workflow system: form → dedup → opportunity → booking → pipeline advance.

Workflow A: Lead Intake

Form Search Contact Create/Update Create Opportunity Check Slots Book Send SMS

Workflow B: Appointment Follow-Up

Appt Completed Search Opp Advance Stage Log Note

Workflow C: Deal Won

Opp Won Tag "customer" Send Welcome

Endpoints Used (9 Total via Custom Webhooks)

POST /contacts/search · POST /contacts/upsert · POST /contacts/{id}/tags · POST /opportunities/ · PUT /opportunities/{id} · GET /opportunities/search · GET /calendars/{id}/free-slots · POST /calendars/events/appointments · POST /conversations/messages

Key Insight: You built a system that agencies charge $2,000+ for. Using only GHL.

Custom Webhook Quick Reference

Setup

Location: Workflow → Add Action → Send Data → Custom Webhook
Cost: $0.01/execution (100 free included)

Required Headers

HeaderValueNotes
AuthorizationBearer YOUR_TOKENAlways required
Version2021-07-28⚠️ 2021-04-15 for Conversations
Content-Typeapplication/jsonFor POST/PUT requests

The 10 Most Useful Endpoints

#MethodEndpointUse
1POST/contacts/searchFind contacts
2POST/contacts/upsertCreate/update contact
3POST/contacts/{id}/tagsAdd tags
4POST/contacts/{id}/notesCreate notes
5POST/opportunities/Create deal
6PUT/opportunities/{id}Update stage/value
7GET/opportunities/searchFind deals
8GET/calendars/{id}/free-slotsCheck availability
9POST/calendars/events/appointmentsBook appointment
10POST/conversations/messagesSend SMS/Email ⚠️
⚠️

THE VERSION HEADER TRAP

Different endpoints require different Version headers!

Endpoint GroupVersion Header
Most endpoints2021-07-28
Conversations endpoints2021-04-15
Calendar endpoints2021-04-15

Using the wrong version header is the #1 cause of mysterious 400 errors.

When in doubt, check the official docs for each endpoint's required version.

Chapter 3

GHL + n8n

Adding a Middleware Brain
RECIPE 3.1

2-Way SMS with Custom Gateway

🟡 Intermediate ⚡ + n8n

What You'll Build

Send SMS through any carrier (not Twilio), receive replies back in GHL conversations.

Architecture

GHL Custom Webhook n8n SMS Gateway Customer
Customer Gateway Webhook n8n GHL Inbound

The Recipe (Outbound)

  1. GHL Workflow: Outbound Message trigger
  2. Custom Webhook → POST to n8n webhook URL
  3. n8n receives → calls SMS gateway API (rbsoft/Semaphore/any provider)

The Recipe (Inbound)

  1. SMS gateway webhooks reply to n8n
  2. n8n: POST /contacts/search (find contact by phone)
  3. n8n: POST /conversations/ (create conversation if needed)
  4. n8n: POST /conversations/messages/inbound (log message, Version: 2021-04-15)
RECIPE 3.2

AI Lead Scoring Engine

🟡 Intermediate ⚡ + n8n

What You'll Build

Auto-enrich and score leads, then route to the appropriate pipeline stage based on score.

Ingredients

  • POST /locations/{locationId}/customFields — create score fields
  • PUT /contacts/{contactId} — write score back
  • POST /contacts/{id}/tags — tier tag: hot/warm/cold
  • POST /opportunities/ — create in appropriate stage

The Recipe

  1. n8n Webhook receives GHL ContactCreate event
  2. n8n: Call Apollo/Clearbit for enrichment data
  3. n8n Function: Calculate score (0–100) based on company size, title, revenue
  4. n8n: PUT /contacts/{id} → write lead_score custom field
  5. n8n Switch node:
    • Score ≥ 80 → Hot path
    • Score ≥ 50 → Warm path
    • Below 50 → Cold path
  6. Hot: POST /opportunities/ (Qualified stage) + enroll in hot-lead workflow
  7. Cold: Tag "long-term-drip" only
RECIPE 3.3

Batch Contact Processing

🔴 Advanced ⚡ + n8n

What You'll Build

Nightly batch job that enriches and scores your entire contact database.

The Recipe

  1. n8n Schedule Trigger: daily at 2 AM
  2. POST /contacts/search: filter enrichment_status != "complete"
  3. SplitInBatches: 10 contacts at a time
  4. For each: enrich → score → write back via PUT /contacts/{id}
  5. 700ms delay between batches (rate limit safe: ~85 req/min)
  6. On error: tag "enrichment-failed" and continue to next
⚠️ Rate Limit Warning: GHL allows ~100 requests/min per location. Always add delays in batch operations. The 700ms delay keeps you safely under the limit at ~85 req/min.
Pattern: Schedule Trigger → Search → SplitInBatches → Process → Delay → Loop. This pattern works for any batch operation against the GHL API.
RECIPE 3.4

Voice AI Appointment Setter (Vapi)

🔴 Advanced ⚡ + n8n 🤖 Vapi

What You'll Build

An AI phone agent that checks your GHL calendar and books appointments mid-call.

Architecture

GHL Tag n8n Vapi Call n8n Function GHL Calendar Book

Ingredients

  • GET /contacts/{contactId} — fetch contact for call
  • GET /calendars/{id}/free-slots — mid-call availability check
  • POST /calendars/events/appointments — book the slot
  • POST /conversations/messages — SMS confirmation
  • PUT /contacts/{id} — write call outcome

The Recipe

  1. GHL: Tag "call-now" → Custom Webhook to n8n
  2. n8n: GET contact details → POST to Vapi create-call API
  3. Vapi mid-call: tool "check_availability" → n8n → GET free-slots → return formatted slots
  4. Vapi: tool "book_appointment" → n8n → POST appointment
  5. Post-call: PUT contacts with call_outcome, POST tags, update pipeline
Key Insight: Vapi's native GHL MCP integration has been broken since late 2025. This n8n middleware pattern is the workaround that actually works.
RECIPE 3.5

Voice AI with Retell (Alternative)

🔴 Advanced ⚡ + n8n 🤖 Retell

What You'll Build

Same appointment-setting pattern as Recipe 3.4, but using Retell's Custom LLM WebSocket interface.

Key Differences: Vapi vs. Retell

FeatureVapiRetell
Latency~1–2sSub-800ms
Voice OptionsElevenLabs, PlayHTBuilt-in voices
SetupSimpler (REST tools)Custom LLM required
Integrationn8n webhook toolsWebSocket + n8n
PricingPer-minutePer-minute

Architecture

Both use n8n as middleware to GHL. The core pattern is identical:

  1. GHL triggers the call via n8n
  2. Voice AI handles conversation
  3. Mid-call tools call n8n → n8n calls GHL API
  4. Post-call: update contact, tags, pipeline

Endpoints: Same as Recipe 3.4

Recommendation: Start with Vapi for simpler setup. Move to Retell if sub-second latency is critical for your use case.

n8n HTTP Request Quick Setup

Reference: Configuring n8n to work with the GHL API

HTTP Request Node Settings

n8n Config
Method: POST / GET / PUT / DELETE
URL: https://services.leadconnectorhq.com/{endpoint}
Authentication: Header Auth
  Name: Authorization
  Value: Bearer {{$credentials.ghlToken}}
Headers:
  Version: 2021-07-28
  Content-Type: application/json
Body: JSON (specify parameters)

Secure Token Storage

Never hardcode tokens. Use n8n's Credentials Store:

  1. Settings → Credentials → Add Credential
  2. Type: Header Auth
  3. Name: Authorization / Value: Bearer YOUR_TOKEN
  4. Reference in nodes via {{$credentials.ghlToken}}

Common n8n Patterns

PatternNodes
Webhook → API CallWebhook → HTTP Request → Respond to Webhook
Scheduled SyncSchedule → HTTP Request → Supabase
Event ProcessingWebhook → Switch → Multiple HTTP Requests
Batch ProcessingSchedule → HTTP Request → SplitInBatches → Wait → HTTP Request

Error Handling

Use the IF node to check HTTP status codes after every API call:

// Check for success
IF {{ $json.statusCode }} == 200 → Continue
ELSE → Error Handler (log to Supabase, retry, or alert)
Chapter 4

GHL + MAKE.COM

Visual Automation Recipes
RECIPE 4.1

Auto Client Onboarding

🔴 Advanced 🔮 + Make

What You'll Build

Form submission → Sub-account creation → Custom field setup → User login → Welcome SMS. Full automated onboarding.

Ingredients

  • POST /locations/ — create sub-account (Agency Pro $497 plan required)
  • POST /oauth/locationToken — generate token for new account
  • POST /locations/{id}/customFields — setup fields
  • POST /contacts/ — create client contact
  • POST /users/ — create user login
  • POST /conversations/messages — welcome SMS

The Recipe

  1. Make: Webhook receives form data from GHL
  2. Make HTTP: POST /locations/ with companyId, name, timezone
  3. Make HTTP: POST /oauth/locationToken → get new location token
  4. Make HTTP: POST /locations/{id}/customFields → create standard fields
  5. Make HTTP: POST /users/ → create user login
  6. Make HTTP: POST /conversations/messages → welcome SMS
Key Insight: Scale from 10 to 100 clients without hiring an onboarding person.
RECIPE 4.2

Cross-Object Associations

🔴 Advanced 🔧 GHL Only

What You'll Build

Link Custom Objects to Opportunities via the Associations API.

Ingredients

  • POST /associations/relations — create the link
  • GET /associations/relations/{recordId} — verify

The Recipe (2-Workflow Pattern)

Workflow 1: Form → Find/Create Opportunity → Create Associated Record (Account object)

Workflow 2: Object Created trigger → Stamp self-ID → Custom Webhook:

POST /associations/relations

{
  "firstRecordId": "account_id",
  "secondRecordId": "opportunity_id",
  "associationId": "static_association_id",
  "locationId": "..."
}
Key Insight: GHL workflows can associate Custom Objects to Contacts natively, but NOT directly to Opportunities. The API is the only way.

Make.com + GHL Setup Reference

HTTP Module Configuration

Make.com HTTP Module
URL: https://services.leadconnectorhq.com/{endpoint}
Method: POST / GET / PUT / DELETE
Headers:
  Authorization: Bearer {{connection.token}}
  Version: 2021-07-28
  Content-Type: application/json
Body type: Raw → JSON
Request content: (your JSON body)

Tips for Make.com Users

  • Use the HTTP module (not a GHL-specific module) for full control
  • Store your token in Make's Data Store or as a custom Connection
  • Use Router modules to branch based on API responses
  • Set Error Handler on every HTTP module → Resume / Retry / Ignore
  • Make's Iterator is equivalent to n8n's SplitInBatches
  • Use Sleep module (700ms) between batch API calls for rate limiting
Make vs. n8n: Make excels at visual debugging and scenario branching. n8n is better for self-hosting and complex data transformations. Both work great with GHL.
Chapter 5

GHL + SUPABASE

Database-Powered Intelligence
RECIPE 5.1

Real-Time KPI Dashboard

🔴 Advanced 🗄️ + Supabase

What You'll Build

Sync GHL data to Supabase, calculate KPIs, display on a live dashboard.

Ingredients

  • POST /contacts/search — sync contacts
  • GET /opportunities/search — sync pipeline
  • GET /calendars/events — sync appointments
  • Supabase Edge Function for KPI calculations

The Recipe

  1. Create Supabase tables: contacts, opportunities, kpi_snapshots
  2. n8n hourly sync: POST /contacts/search → upsert to Supabase
  3. n8n: GET opportunities → upsert to Supabase
  4. Edge Function: calculate win_rate, revenue, avg_response_time
  5. Frontend: Lovable React app with KPI cards + charts
Stack: GHL (data source) → n8n (sync) → Supabase (storage + compute) → Lovable (frontend). This is the production pattern for any GHL analytics project.
RECIPE 5.2

AI Inbox Classifier

🔴 Advanced 🗄️ + Supabase

What You'll Build

AI classifies every inbound message as booking request, inquiry, complaint, or spam — and auto-responds when confident.

Ingredients

  • GHL Webhook: InboundMessage event
  • GET /conversations/{id}/messages — get context
  • POST /conversations/messages — auto-reply
  • POST /contacts/{id}/tags — classification tag

The Recipe

  1. Edge Function: ghl-inbound-handler receives webhook
  2. Parse message → store in Supabase
  3. Call Claude/GPT: classify as booking_request / inquiry / complaint / spam
  4. If confidence > 0.8: POST auto-reply via Conversations API
  5. If low confidence: Tag "needs-human-review"
  6. If booking_request: Tag "book-now" → triggers the auto-booking workflow from Recipe 2.3!
The Chain: Inbound message → AI classifies → Tag "book-now" → Auto-booking workflow fires → Appointment booked → SMS sent. All automated.
RECIPE 5.3

Token Management for Scale

🔴 Advanced 🗄️ + Supabase

What You'll Build

Manage OAuth tokens for 100+ sub-accounts with automatic refresh and failure alerting.

Ingredients

  • POST /oauth/token — refresh tokens
  • POST /oauth/locationToken — generate location tokens

The Recipe

  1. Supabase table: ghl_tokens with columns:
    location_id, access_token, refresh_token, expires_at, status
  2. n8n Cron every 4 hours: query tokens expiring in 6 hours
  3. For each: POST /oauth/token with refresh_token
  4. Update Supabase with new tokens and expiry
  5. If refresh fails 3x → set status = "failed" → Slack alert
Key Insight: This is the backbone. If tokens fail, everything fails. Build this first when scaling beyond a handful of accounts.

Supabase Edge Function Template

Copy-paste starter for any GHL + Supabase integration

GHL API Helper

TypeScript
const GHL_BASE = 'https://services.leadconnectorhq.com';

async function ghlRequest(
  endpoint: string,
  token: string,
  options: RequestInit = {}
) {
  const response = await fetch(
    `${GHL_BASE}${endpoint}`,
    {
      ...options,
      headers: {
        'Authorization': `Bearer ${token}`,
        'Version': '2021-07-28',
        'Content-Type': 'application/json',
        ...options.headers,
      },
    }
  );

  if (!response.ok) {
    throw new Error(
      `GHL API Error: ${response.status}`
    );
  }

  return response.json();
}

Webhook Handler Pattern

TypeScript
import { createClient } from '@supabase/supabase-js';

Deno.serve(async (req) => {
  const body = await req.json();
  const supabase = createClient(
    Deno.env.get('SUPABASE_URL')!,
    Deno.env.get('SUPABASE_SERVICE_KEY')!
  );

  // 1. Store the event
  await supabase
    .from('ghl_events')
    .insert({ payload: body });

  // 2. Enqueue for processing
  await supabase.rpc('pgmq_send', {
    queue: 'ghl_events',
    message: body
  });

  // 3. Return 200 immediately!
  return new Response(
    JSON.stringify({ status: 'queued' }),
    { status: 200 }
  );
});
Chapter 6

CLAUDE MCP + GHL

AI-Powered CRM Analysis
RECIPE 6.1

Connect Claude to GHL

🟡 Intermediate 🤖 Claude MCP

What You'll Build

Claude Desktop connected to your live GHL account via MCP (Model Context Protocol).

The Recipe

  1. Open Claude Desktop → Settings → Developer → MCP Servers
  2. Add the GHL MCP server URL
  3. Configure with your Private Integration Token + locationId
  4. Test: "Search for contacts created in the last 7 days"
  5. Claude calls the MCP tool → returns real contacts from your CRM

Available MCP Tools (21+)

Contacts: search, create, update, delete, add tags, add notes
Calendars: list, get free slots, book appointments
Opportunities: search, create, update, change status
Conversations: search, get messages, send messages
Workflows: list, add contact to workflow

Key Insight: Zero YouTube videos exist about this as of March 2026. You're ahead of everyone.
RECIPE 6.2

Pipeline Health Audit

🟡 Intermediate 🤖 Claude MCP

What You'll Build

Claude audits your entire pipeline, flags stale deals, and cross-references with calendar data.

The Prompt

"Search all open opportunities. Flag any that have
been stuck in the same stage for more than 14 days.
For each stale deal, cross-reference with the
calendar — does the contact have an upcoming
appointment? Give me a prioritized action list."

What Claude Does

  1. Calls opportunities/search → gets all open deals
  2. Analyzes stage timestamps → identifies stale deals (>14 days)
  3. For each stale contact: checks calendar for upcoming appointments
  4. Generates prioritized recommendations:
    Urgent: Stale deal, no appointment booked
    Action needed: Stale deal, appointment next week
    On track: Appointment tomorrow
Time saved: This analysis would take 30–60 minutes manually. Claude does it in under 2 minutes.
RECIPE 6.3

Conversation Summarizer

🟡 Intermediate 🤖 Claude MCP

What You'll Build

Claude reads and summarizes all recent conversations, giving you a quick status overview.

The Prompt

"Get the last 20 conversations. For each one,
summarize the key points and classify the status
as: active, waiting-for-reply, or closed.
Group them by status and highlight any that
need immediate attention."

What Claude Returns

A structured report grouped by status:

  • Needs Attention (3): Unread messages waiting >24 hours
  • Waiting for Reply (8): You sent last message, awaiting response
  • Active (5): Back-and-forth conversations in progress
  • Closed (4): Resolved or no response in 7+ days
Key Insight: 5 reports that would take half a day. Done in 10 minutes. The MCP pattern turns Claude into your CRM analyst.
Chapter 7

WEBHOOKS & EVENTS

React to Everything in Real-Time

Webhook Events Reference

CategoryEventFires When
ContactContactCreateNew contact created
ContactUpdateContact fields changed
ContactDeleteContact removed
ContactTagUpdateTags added/removed
ContactDndUpdateDND status changed
ConversationInboundMessageMessage received
OutboundMessageMessage sent
ConversationUnreadUpdateUnread count changed
AppointmentAppointmentCreateAppointment booked
AppointmentUpdateAppointment modified
AppointmentDeleteAppointment cancelled
OpportunityOpportunityCreateDeal created
OpportunityUpdateDeal modified
OpportunityStageUpdateStage changed
OpportunityStatusUpdateWon/Lost/Open changed
OpportunityMonetaryValueUpdateDeal value changed
Notes/TasksNoteCreateNote added
TaskCreateTask created
TaskCompleteTask marked done
RECIPE 7.1

Universal Event Router

🔴 Advanced 🗄️ + Supabase

What You'll Build

One Edge Function that routes ALL GHL webhook events to the right handler.

The Pattern

TypeScript
// ghl-event-router
const handlers: Record<string, Function> = {
  'InboundMessage':       handleMessage,
  'ContactCreate':        handleNewLead,
  'AppointmentStatus':    handleAppointment,
  'OpportunityStageUpdate': handlePipeline,
};

Deno.serve(async (req) => {
  const body = await req.json();
  const eventType = body.type;

  // Enqueue to pgmq, return 200 immediately
  await supabase.rpc('pgmq_send', {
    queue: eventType,
    message: body
  });

  return new Response('OK', { status: 200 });
});
Critical Rule: Never block a webhook handler. Enqueue and return 200 immediately. Process in background. GHL will retry if you don't respond in time — causing duplicate processing.

Production Architecture

The 4-Layer Stack for GHL API Projects

LAYER 1
Frontend
Lovable React App · Dashboard UI
LAYER 2
Database
Supabase · pgvector · pgmq
LAYER 3
Middleware
n8n · Edge Functions · Make.com
LAYER 4
Data Source
GoHighLevel API · 413 Endpoints
The Pattern: GHL is your system of record. Supabase is your analytical layer. n8n/Make connects them. Your frontend reads from Supabase — never directly from GHL.
Chapter 8

ENDPOINT REFERENCE

413 Endpoints at Your Fingertips

Contacts Endpoints

MethodEndpointDescription
POST/contacts/Create Contact
GET/contacts/:idGet Contact
PUT/contacts/:idUpdate Contact
DELETE/contacts/:idDelete Contact
POST/contacts/upsertCreate or Update
POST/contacts/searchSearch (recommended)
GET/contacts/search/duplicateCheck Duplicates
POST/contacts/:id/tagsAdd Tags
DELETE/contacts/:id/tagsRemove Tags
POST/contacts/:id/notesCreate Note
POST/contacts/:id/workflow/:wfIdAdd to Workflow
POST/contacts/:id/campaigns/:campIdAdd to Campaign

Conversations Endpoints

⚠️ Version: 2021-04-15 for ALL conversation endpoints!
MethodEndpointDescription
POST/conversations/messagesSend Message (SMS, Email, WhatsApp)
POST/conversations/messages/inboundLog Inbound Message
GET/conversations/:id/messagesGet Messages
GET/conversations/searchSearch Conversations
POST/conversations/Create Conversation
Message Types: The /conversations/messages endpoint can send SMS, Email, and WhatsApp messages. Set the type field to "SMS", "Email", or "WhatsApp" in the request body.

Calendars & Opportunities

Calendars

MethodEndpointDescription
GET/calendars/List Calendars
GET/calendars/:id/free-slotsCheck Availability
POST/calendars/services/bookingsBook Appointment

Opportunities

MethodEndpointDescription
POST/opportunities/Create Opportunity
PUT/opportunities/:idUpdate Stage/Value
PUT/opportunities/:id/statusUpdate Status
GET/opportunities/pipelinesList Pipelines
POST/opportunities/upsertCreate or Update

Locations & Users

Locations (Sub-Accounts)

MethodEndpointDescription
GET/locations/searchSearch Sub-Accounts
POST/locations/Create Sub-Account (Agency Pro only)
GET/locations/:id/customFieldsGet Custom Fields
POST/locations/:id/customFieldsCreate Custom Field

Users

MethodEndpointDescription
GET/users/Get Users by Location
POST/users/Create User
PUT/users/:idUpdate User

OAuth & Advanced

OAuth

MethodEndpointDescription
POST/oauth/tokenGet/Refresh Token
POST/oauth/locationTokenGet Location Token
GET/oauth/installedLocationsList Installed Locations

Associations

MethodEndpointDescription
POST/associations/relationsCreate Relation
GET/associations/relations/:recordIdGet Relations
Token Lifecycle:
1. User installs your app → you get an authorization_code
2. Exchange code for access_token + refresh_token via POST /oauth/token
3. Access token expires in ~24 hours
4. Use refresh_token to get a new access token
5. For sub-account operations: POST /oauth/locationToken with companyId + locationId

Additional Endpoints

CategoryMethodEndpointDescription
WorkflowsGET/workflows/List Workflows
CampaignsGET/campaigns/List Campaigns
FunnelsGET/funnels/funnel/listList Funnels
ProductsGET/products/listList Products
BlogsGET/blogs/listList Blog Posts
BlogsPOST/blogs/postCreate Blog Post
SurveysGET/surveysList Surveys
SnapshotsGET/snapshotsList Snapshots
Brand BoardsGET/brand-boardsGet Brand Boards
Full Documentation: The complete 413+ endpoint reference is available at the official GHL marketplace docs. This reference covers the most commonly used endpoints for automation workflows.

Scopes Reference

ScopeAccessDescription
contacts.readonlyReadView contacts & search
contacts.writeWriteCreate, update, delete contacts
conversations.readonlyReadView conversations
conversations.writeWriteCreate conversations
conversations/message.readonlyReadView messages
conversations/message.writeWriteSend messages
calendars.readonlyReadView calendars & slots
calendars.writeWriteBook & manage appointments
opportunities.readonlyReadView pipeline & deals
opportunities.writeWriteCreate & update deals
locations.readonlyReadView sub-accounts
locations.writeWriteCreate sub-accounts
users.readonlyReadView users
users.writeWriteCreate & manage users
workflows.readonlyReadView workflows
Principle of Least Privilege: Only request the scopes your integration actually needs. You can always add more later via Settings → Integrations.
Chapter 9

TROUBLESHOOTING

When Things Go Wrong

Common Errors

ErrorCauseFix
401 Unauthorized Token expired or invalid Refresh token via POST /oauth/token
422 Unprocessable Missing required fields Check request body — locationId is often missing
400 Bad Request Missing or wrong Version header Add correct Version header (see page 19)
429 Rate Limited Too many requests Add delays; max ~100 req/min per location
404 Not Found Wrong ID or endpoint path Verify resource IDs exist; check endpoint URL
403 Scope Error Missing permission Add required scope in app settings

Debug Checklist

  1. ✅ Is the Authorization header present? (Bearer YOUR_TOKEN)
  2. ✅ Is the Version header correct for this endpoint?
  3. ✅ Is Content-Type: application/json set for POST/PUT?
  4. ✅ Is locationId included in the body?
  5. ✅ Are all required fields present?
  6. ✅ Is the token still valid (not expired)?
  7. ✅ Do you have the right scope for this endpoint?

Rate Limits & Best Practices

Rate Limits

  • ~100 requests/min per location (not officially documented, observed)
  • Implement exponential backoff on 429 responses
  • Batch operations: use 700ms delays between requests (~85/min, safe margin)
  • If you hit 429: wait 2^attempt seconds before retry (1s, 2s, 4s, 8s...)

Token Best Practices

  • Access tokens expire in ~24 hours
  • Always store and use refresh_token — it doesn't expire
  • Refresh proactively (before expiry), not reactively (after 401)
  • For multi-account: build token management (Recipe 5.3)

Webhook Best Practices

  • Always return 200 OK from webhook handlers immediately
  • Enqueue the event for async processing (pgmq, Redis, SQS)
  • Implement idempotency — webhooks can fire multiple times
  • Log everything to Supabase for debugging

General Best Practices

  • Use upsert over create → avoids duplicates
  • Always include error handling in workflows
  • Test every API call in the Playground first
  • Tag API-created records for tracking
  • Never store tokens in code — use env variables or credentials store
Chapter 10

WHAT'S NEXT

Your Journey Continues

Resources & Next Steps

Essential Links

ResourceURL
GHL API Playgroundbuildaischool.com/gohighlevel-api
Official API Docsmarketplace.gohighlevel.com/docs/
BuildAI Schoolbuildaischool.com
SmartChatsmartchat.ph

What You've Learned

  • ✅ API authentication and the Version Header system
  • ✅ The Custom Webhook pattern (GHL calling its own API)
  • ✅ n8n middleware for 2-way SMS, lead scoring, voice AI
  • ✅ Make.com for visual automation and client onboarding
  • ✅ Supabase for KPI dashboards, AI classification, token management
  • ✅ Claude MCP for AI-powered CRM analysis
  • ✅ Production architecture and best practices

Keep Building

Subscribe to the BuildAI School newsletter for weekly API recipes, new endpoint discoveries, and production patterns from real client projects.

You went from "what's a Custom Webhook?" to understanding production AI systems. Keep building. Keep shipping. The API is your unfair advantage.
The GoHighLevel API Cookbook
Built by developers,
for developers.
BuildAI School  |  SmartChat
© 2026 Julius "Jenius with a J" Guevarra
Created with Perplexity Computer
Page 1 of 59
Page 1 of 59