In short
- Zapier: fastest to set up, most app integrations, highest per-task cost at volume.
- Make (formerly Integromat): visual, flexible, cheaper than Zapier at scale, GHL native module available.
- n8n: open source, self-hostable, no per-task fees, requires more technical setup.
- Direct API: full control, cheapest at volume, requires code, handles edge cases that no-code tools cannot.
Endpoints
| Method | Path | Scopes | Rate limit |
|---|---|---|---|
| Various | https://services.leadconnectorhq.com/* | Depends on actions | See GHL API docs |
| POST | https://hooks.zapier.com/hooks/catch/... (Zapier webhook) | n/a | Zapier plan limits |
| POST | https://hook.make.com/... (Make webhook) | n/a | Make plan limits |
| POST | https://your-n8n.com/webhook/... (n8n webhook) | n/a | Your server limits |
Authentication
All four approaches eventually call the GHL API with a Bearer token. The difference is who manages the token: the no-code platform stores it in a connection, while direct API code stores it in your environment variables.
No-code platforms like Zapier and Make handle OAuth refresh automatically. If you write direct API calls, you need to implement token refresh yourself for OAuth apps, or use a long-lived private integration token.
When using Zapier or Make with the GHL module, connect the integration once per sub-account through the GHL OAuth flow. The platform stores the token securely and refreshes it on expiry.
Recipes
Zapier: new GHL contact triggers a row in Google Sheets
The Zapier GHL integration (by LeadConnector) has trigger and action modules for contacts, opportunities, and appointments. This is the fastest path for simple one-way syncs with non-technical clients.
Limitations: tasks are billed per run, Zapier does not support conditional logic as well as Make or n8n, and some GHL events are not available as triggers. Check the current Zap template list for up-to-date coverage.
Zapier Zap structure (no code)
Trigger: GoHighLevel, New Contact
Connection: YOUR_GHL_ACCOUNT
Filter (optional): Only continue if tags contains "qualified"
Action: Google Sheets, Create Spreadsheet Row
Spreadsheet: Lead Tracker
Sheet: Contacts
Fields:
, First Name: {{contact.firstName}}
, Last Name: {{contact.lastName}}
, Email: {{contact.email}}
, Phone: {{contact.phone}}
, Date Added: {{contact.dateAdded}}Make: GHL opportunity won triggers Slack message and invoice
Make (formerly Integromat) has a native GoHighLevel module and supports more complex logic with routers, iterators, and error handlers than Zapier. Good for multi-step flows with conditional branching.
Make operations are cheaper than Zapier tasks at volume and the visual canvas is easier to debug. The free tier includes a limited number of operations per month.
Make scenario structure (no code)
Trigger: GoHighLevel, Watch Opportunities
Filter: status = "won"
Router:
Branch 1 (always):
Action: Slack, Create a Message
Channel: #sales-wins
Message: "New win: {{opportunity.name}}, USD {{opportunity.monetaryValue}}"
Branch 2 (if monetaryValue > 1000):
Action: QuickBooks, Create Invoice
Customer: {{contact.firstName}} {{contact.lastName}}
Email: {{contact.email}}
Amount: {{opportunity.monetaryValue}}Direct API: create a contact from a custom form and notify Slack
Write a small serverless function that handles a form POST, calls the GHL API to create the contact, then calls the Slack API to post a notification. This is the right call when you need custom validation, error handling, or transformations that no-code tools cannot do cleanly.
Direct API code is also cheaper at high volume (no per-task fees beyond your hosting costs) and gives you full control over retry logic and error handling.
Node.js (Vercel serverless)
export async function POST(req) {
const body = await req.json();
const { name, email, phone, source } = body;
// 1. Validate input
if (!email && !phone) {
return Response.json({ error: 'Email or phone required' }, { status: 400 });
}
// 2. Create GHL contact
const ghlRes = await fetch('https://services.leadconnectorhq.com/contacts', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.GHL_TOKEN}`,
'Content-Type': 'application/json',
Version: '2021-07-28',
},
body: JSON.stringify({
locationId: process.env.GHL_LOCATION_ID,
email,
phone,
firstName: name?.split(' ')[0],
lastName: name?.split(' ').slice(1).join(' '),
source,
tags: ['form-lead'],
}),
});
if (!ghlRes.ok) {
const err = await ghlRes.text();
console.error('GHL error:', err);
return Response.json({ error: 'Failed to create contact' }, { status: 500 });
}
const { contact } = await ghlRes.json();
// 3. Notify Slack
await fetch(process.env.SLACK_WEBHOOK_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
text: `New lead from ${source}: ${name} (${email})`,
}),
});
return Response.json({ contactId: contact.id });
}Python (FastAPI)
import os
import requests
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
GHL_TOKEN = os.environ["GHL_TOKEN"]
GHL_LOCATION_ID = os.environ["GHL_LOCATION_ID"]
SLACK_WEBHOOK = os.environ["SLACK_WEBHOOK_URL"]
class LeadForm(BaseModel):
name: str
email: str | None = None
phone: str | None = None
source: str = "website"
@app.post("/submit-lead")
def submit_lead(form: LeadForm):
if not form.email and not form.phone:
return {"error": "Email or phone required"}, 400
parts = form.name.split(" ", 1)
ghl_res = requests.post(
"https://services.leadconnectorhq.com/contacts",
headers={"Authorization": f"Bearer {GHL_TOKEN}", "Version": "2021-07-28"},
json={
"locationId": GHL_LOCATION_ID,
"email": form.email,
"phone": form.phone,
"firstName": parts[0],
"lastName": parts[1] if len(parts) > 1 else "",
"tags": ["form-lead"],
},
)
ghl_res.raise_for_status()
contact_id = ghl_res.json()["contact"]["id"]
requests.post(SLACK_WEBHOOK, json={"text": f"New lead: {form.name} ({form.email})"})
return {"contactId": contact_id}Common errors
- Zapier task limits hit: you are running more tasks per month than your plan allows. Either upgrade or migrate high-volume triggers to Make or a direct API function.
- Make operation timeout: a scenario step is taking longer than Make's per-operation timeout. Break the scenario into smaller ones or move the slow step to a direct API call.
- n8n webhook not receiving events: the n8n instance is not publicly accessible. Use a reverse proxy (Caddy, Nginx) or a tunnel (ngrok) during development. In production, deploy n8n with a public URL.
- Direct API: silent failures with no retry: unlike no-code platforms, direct API code does not retry by default. Add try/catch and an exponential back-off loop for any GHL API call.
MCP tool definition
MCP tool definition (JSON)
{
"name": "route_integration_decision",
"description": "Given a GoHighLevel integration requirement, recommend whether to use Zapier, Make, n8n, or direct API calls.",
"inputSchema": {
"type": "object",
"properties": {
"monthlyRunVolume": { "type": "number", "description": "Expected runs per month" },
"technicalLevel": {
"type": "string",
"enum": ["non-technical", "some-code", "full-code"]
},
"needsWebhookVerification": { "type": "boolean" },
"selfHosted": { "type": "boolean" }
},
"required": ["monthlyRunVolume", "technicalLevel"]
}
}