GoHighLevel's API and webhook system is one of its most powerful features, letting you connect virtually any external system to your GHL account. After 6+ years of using GoHighLevel since 2019, I've built dozens of custom integrations - from simple WordPress form connections to complex multi-system workflows that handle everything automatically.
This guide will walk you through everything you need to know to start building your own integrations, whether you're a seasoned developer or just getting started with APIs.
GoHighLevel API Overview
GoHighLevel uses a REST API v2 that gives you programmatic access to virtually every feature in the platform. You can create and update contacts, manage opportunities through your sales pipeline, send messages, book appointments, trigger campaigns, and much more.
The API is built around standard HTTP methods (GET, POST, PUT, DELETE) and returns JSON responses. Here's what you can do:
- Contact Management: Create, update, delete, and search contacts
- Pipeline Management: Add opportunities, move deals through stages, update values
- Calendar Integration: Book appointments, check availability, manage calendar events
- Communication: Send SMS, emails, and voicemails programmatically
- Campaign Management: Add contacts to campaigns, trigger automations
- Custom Fields: Update any custom fields you've created in your account
Authentication Methods
GoHighLevel offers three main authentication methods, each suited for different use cases:
OAuth 2.0 (Recommended for Most Cases)
OAuth 2.0 is the gold standard for API authentication and what I recommend for most integrations. It's required if you want to:
- Receive webhooks from GoHighLevel
- Build apps for the GoHighLevel Marketplace
- Access multiple locations from a single integration
- Share your integration with other users
Here's the OAuth flow:
- Register your app in GoHighLevel's developer portal
- Redirect users to the authorization URL with your client ID and scopes
- GoHighLevel redirects back to your app with an authorization code
- Exchange the code for access and refresh tokens
- Use the access token in API calls (expires every ~24 hours)
- Use the refresh token to get new access tokens automatically
The authorization URL looks like this:
https://marketplace.gohighlevel.com/oauth/chooselocation?response_type=code&redirect_uri=YOUR_CALLBACK_URL&client_id=YOUR_CLIENT_ID&scope=contacts.readonly+contacts.write+opportunities.write
Agency API Keys
Agency API keys are perfect for managing sub-accounts programmatically. You can generate these in your Agency Settings > API Keys. They're long-lived and scoped to agency-level operations, but they don't support webhooks.
Private Integration Tokens
For simple, single-location integrations that don't need webhooks, private tokens are the easiest option. Generate them in your location settings and use them directly in API calls.
Key API Endpoints
The GoHighLevel API is organized around the main objects in the system. Here are the endpoints you'll use most often:
Contacts API
Base endpoint: GET/POST/PUT https://services.leadconnectorhq.com/contacts/
The contacts API is probably what you'll use most. You can create new contacts, update existing ones, search by email or phone, and manage tags and custom fields.
Opportunities API
Base endpoint: GET/POST/PUT https://services.leadconnectorhq.com/opportunities/
This handles your sales pipeline. Create new deals, move opportunities between stages, update monetary values, and assign to team members.
Conversations API
Base endpoint: GET/POST https://services.leadconnectorhq.com/conversations/
Send SMS messages, emails, and manage conversation threads. Essential for automated follow-up sequences.
Calendars API
Base endpoint: GET/POST https://services.leadconnectorhq.com/calendars/
Book appointments, check availability, and manage calendar events. Great for integrating with external booking systems.
Creating and Updating Contacts
Contact management is the foundation of most GoHighLevel integrations. Here's how to create a new contact using curl:
curl -X POST "https://services.leadconnectorhq.com/contacts/" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"firstName": "John",
"lastName": "Doe",
"email": "john@example.com",
"phone": "+1234567890",
"tags": ["lead", "website"],
"customFields": [
{
"id": "custom_field_id",
"value": "Custom Value"
}
]
}'
To update an existing contact, use PUT with the contact ID:
curl -X PUT "https://services.leadconnectorhq.com/contacts/CONTACT_ID" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"firstName": "Jane",
"tags": ["lead", "website", "qualified"]
}'
GET /contacts/search?email=john@example.comPipeline and Opportunity Management
Managing your sales pipeline through the API is incredibly powerful for workflows for agencies. Here's how to create a new opportunity:
curl -X POST "https://services.leadconnectorhq.com/opportunities/" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"title": "Website Redesign Project",
"stageId": "stage_id_here",
"pipelineId": "pipeline_id_here",
"contactId": "contact_id_here",
"monetaryValue": 5000,
"assignedTo": "user_id_here"
}'
To move an opportunity to a different stage:
curl -X PUT "https://services.leadconnectorhq.com/opportunities/OPPORTUNITY_ID" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"stageId": "new_stage_id_here"
}'
I use this heavily in my agency to automatically move deals based on client actions - like moving to "Contract Sent" when a proposal is generated, or "Won" when payment is received.
Webhook Setup and Configuration
Webhooks are where GoHighLevel's automation really shines. There are two types:
Inbound Webhooks (Triggering GHL Workflows)
Inbound webhooks let external systems trigger GoHighLevel workflows. Every workflow can have a webhook trigger that looks like this:
https://hooks.leadconnectorhq.com/v1/hooker/XXXXX?company_id=YYYY
To trigger a workflow from an external system:
curl -X POST "https://hooks.leadconnectorhq.com/v1/hooker/YOUR_HOOK_ID?company_id=YOUR_COMPANY_ID" \
-H "Content-Type: application/json" \
-d '{
"firstName": "Alice",
"email": "alice@example.com",
"customData": "any value you want"
}'
The data you send becomes available as variables in your workflow, which you can use in follow-up actions.
Outbound Webhooks (GHL Notifying External Systems)
Outbound webhooks send data from GoHighLevel to external systems when specific events happen. You set these up in your automation workflows using the "Webhook" action.
GoHighLevel signs outbound webhooks for security. Always verify the signature to prevent spoofing:
// Node.js example
const crypto = require('crypto');
function verifyWebhook(payload, signature, publicKey) {
const payloadBuffer = Buffer.from(payload, 'utf8');
const signatureBuffer = Buffer.from(signature, 'base64');
return crypto.verify(null, payloadBuffer, publicKey, signatureBuffer);
}
Common Webhook Events
Here are the webhook events I use most often in my integrations:
Contact Created
{
"type": "ContactCreate",
"contactId": "contact_123",
"firstName": "John",
"lastName": "Doe",
"email": "john@example.com",
"phone": "+1234567890",
"tags": ["lead"],
"source": "Website Form"
}
Opportunity Stage Changed
{
"type": "OpportunityStageUpdate",
"opportunityId": "opp_456",
"contactId": "contact_123",
"pipelineId": "pipeline_789",
"stageId": "stage_abc",
"stageName": "Proposal Sent",
"monetaryValue": 5000
}
Appointment Booked
{
"type": "AppointmentBooked",
"appointmentId": "appt_999",
"contactId": "contact_123",
"calendarId": "cal_888",
"startTime": "2024-01-15T14:00:00Z",
"endTime": "2024-01-15T15:00:00Z",
"title": "Discovery Call"
}
Zapier Integration
While you can build direct API integrations, sometimes Zapier is the faster option, especially for simple workflows. Use Zapier when:
- You need to connect GoHighLevel to popular apps (Gmail, Slack, Shopify)
- The integration is simple (trigger → action)
- You want to avoid custom development
- Non-technical team members need to modify the workflow
To connect GoHighLevel to Zapier:
- Create a new Zap in Zapier
- Choose "Webhooks by Zapier" as your trigger
- Select "Catch Hook" and copy the webhook URL
- In GoHighLevel, add a "Webhook" action to your workflow
- Paste the Zapier URL and configure the data to send
- Test the connection and configure your Zapier action
Make.com Integration
Make.com (formerly Integromat) is more powerful than Zapier for complex workflows. It's my preferred choice when I need conditional logic, loops, or data transformation.
To set up a Make.com integration:
- Add a "Webhooks" module in Make
- Choose "Custom webhook" and copy the URL
- In GoHighLevel, add the Make webhook URL to your workflow
- For agency/sub-account setups, use an HTTP module to exchange agency tokens for location tokens
Make.com is particularly powerful for agency setups because you can handle multi-location authentication and routing in a single scenario.
Rate Limits and Best Practices
GoHighLevel implements rate limiting to ensure platform stability. While the exact limits aren't publicly documented, here are best practices I follow:
- Batch operations: Group multiple updates into single API calls when possible
- Implement retries: Use exponential backoff for 429 (rate limit) responses
- Cache data: Don't repeatedly fetch the same data; cache it locally
- Use webhooks: Instead of polling for changes, let GoHighLevel notify you
- Off-peak processing: Schedule heavy operations during low-usage hours
Error Handling and Debugging
Robust error handling is crucial for production integrations. Here's my standard error handling pattern:
async function ghlApiCall(endpoint, data) {
try {
const response = await fetch(`https://services.leadconnectorhq.com${endpoint}`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});
if (!response.ok) {
if (response.status === 401) {
// Token expired, refresh it
await refreshAccessToken();
return ghlApiCall(endpoint, data); // Retry
}
throw new Error(`API call failed: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('GHL API Error:', error);
// Log to your error tracking service
throw error;
}
}
For debugging, I always:
- Log all API requests and responses (excluding sensitive data)
- Use webhook.site for testing webhook payloads
- Check GoHighLevel's system status page during outages
- Validate webhook signatures in development environments
Three Practical Integration Examples
Example 1: WordPress Form to GoHighLevel
This is probably the most common integration I build. Here's a PHP snippet for WordPress:
function send_to_ghl($form_data) {
$webhook_url = 'https://hooks.leadconnectorhq.com/v1/hooker/YOUR_HOOK_ID?company_id=YOUR_COMPANY_ID';
$data = array(
'firstName' => $form_data['first_name'],
'lastName' => $form_data['last_name'],
'email' => $form_data['email'],
'phone' => $form_data['phone'],
'source' => 'Contact Form'
);
wp_remote_post($webhook_url, array(
'headers' => array('Content-Type' => 'application/json'),
'body' => json_encode($data)
));
}
In GoHighLevel, create a workflow triggered by this webhook that adds the contact and starts your follow-up sequence.
Example 2: GoHighLevel to Slack Notifications
Get instant notifications when important events happen in your pipeline:
// Outbound webhook from GHL to your server
app.post('/ghl-webhook', (req, res) => {
const { type, opportunity, contact } = req.body;
if (type === 'OpportunityWon') {
const message = {
text: `🎉 New sale! ${contact.firstName} ${contact.lastName} - $${opportunity.monetaryValue}`,
channel: '#sales'
};
// Send to Slack
fetch(SLACK_WEBHOOK_URL, {
method: 'POST',
body: JSON.stringify(message)
});
}
res.sendStatus(200);
});
Example 3: Custom Dashboard Integration
Build a custom dashboard that pulls data from GoHighLevel's API:
async function fetchDashboardData() {
const [contacts, opportunities] = await Promise.all([
fetch('/contacts?limit=100', { headers: authHeaders }),
fetch('/opportunities?limit=100', { headers: authHeaders })
]);
const contactsData = await contacts.json();
const opportunitiesData = await opportunities.json();
return {
totalContacts: contactsData.meta.total,
newContactsThisMonth: contactsData.contacts.filter(isThisMonth).length,
pipelineValue: opportunitiesData.opportunities.reduce((sum, opp) =>
sum + opp.monetaryValue, 0
)
};
}
This type of integration is particularly valuable for agencies using the white-label guide who want to provide custom reporting to clients.
Bottom Line
GoHighLevel's API and webhook system opens up unlimited integration possibilities. Whether you're connecting a simple contact form or building complex multi-system workflows, the foundation is solid and well-documented.
Start small with a single integration - maybe connecting your website forms to GoHighLevel - then expand from there. The OAuth 2.0 authentication might seem complex initially, but it's worth the extra setup for the security and flexibility it provides.
Remember that sometimes the simplest solution is the best one. Before building a custom integration, check if GoHighLevel already has native integration with your desired platform, or if a tool like Zapier can handle the job with less development time.
The API continues to evolve, so always refer to the official documentation for the latest endpoints and features. With these fundamentals in place, you'll be able to create powerful automations that save time and improve your client experience.