GoHighLevel API and Webhooks: Custom Integrations

Complete developer guide to GoHighLevel API and webhooks. Build custom integrations, sync data with external systems, and extend GHL capabilities with REST API and webhook automation.

E
ECOSIRE Research and Development Team
|March 19, 202611 min read2.4k Words|

GoHighLevel API and Webhooks: Custom Integrations

GoHighLevel's native integrations and Zapier connectors cover most standard use cases, but businesses with specific workflows, proprietary systems, or high data volumes eventually need to work directly with GHL's API. The GHL REST API and webhook system give developers full programmatic access to contacts, pipelines, campaigns, appointments, and more — enabling integrations that no-code tools simply can't replicate.

This guide is written for technical teams building custom integrations with GoHighLevel. It covers the API architecture, authentication, key endpoints, webhook setup and security, and practical integration patterns for common use cases.

Key Takeaways

  • GHL's REST API (v2) uses OAuth 2.0 for authentication and supports all major CRM objects
  • Inbound webhooks in GHL workflows let external systems trigger GHL automations in real time
  • Outbound webhooks from GHL notify external systems when GHL events occur (contact created, pipeline updated, etc.)
  • Rate limits are 100 requests/10 seconds per location — batch operations and caching are important at scale
  • The GHL Marketplace allows you to publish integrations as GHL-native apps for customer installation
  • Custom values and custom fields are the primary data extension points for storing integration state
  • Webhook payload verification using the GHL signature header prevents spoofed requests
  • Most GHL integrations follow four patterns: contact sync, event-triggered automation, pipeline bridging, or report aggregation

GHL API Architecture Overview

GoHighLevel's API (version 2 as of 2026) is a standard REST API with JSON request and response bodies. The base URL is:

https://services.leadconnectorhq.com

The API organizes resources into these primary namespaces:

ResourceEndpointsCommon Use
/contacts/CRUD + search + tagsContact sync, lead creation
/opportunities/CRUD + pipeline opsDeal management
/calendars/Events + availabilityBooking integration
/campaigns/Email/SMS campaignsCampaign management
/conversations/Messages + threadsCommunication history
/locations/Sub-account configAgency management
/forms/Form submissionsLead capture processing
/workflows/Trigger enrollmentWorkflow automation
/users/User managementTeam provisioning
/custom-fields/Field configData structure management

The full API documentation is available at https://highlevel.stoplight.io/docs/integrations/.


Authentication: OAuth 2.0 Setup

GHL's API uses OAuth 2.0 for all authentication. There are two authentication contexts:

1. Agency-Level API Key (for sub-account management)

For server-to-server integrations that manage multiple sub-accounts, use the Agency API Key:

  • Generated in Agency Settings > API Keys
  • Scoped to agency-level operations (creating/managing sub-accounts)

2. Sub-Account OAuth (for per-location integrations)

For integrations operating within a single sub-account (the most common case):

Authorization Flow:
1. Register your app in GHL Marketplace (or use a private integration key)
2. Redirect user to GHL OAuth consent page:
   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+...
3. User approves → GHL redirects to your callback with ?code=AUTH_CODE
4. Exchange auth code for access + refresh tokens:
   POST https://services.leadconnectorhq.com/oauth/token
   Body: {
     client_id, client_secret, grant_type: "authorization_code",
     code: AUTH_CODE, redirect_uri: YOUR_CALLBACK_URL
   }
5. Use access token in Authorization header: Bearer ACCESS_TOKEN
6. Refresh access token when expired (typically every 24 hours) using refresh token

Scopes Required for Common Operations:

ScopePurpose
contacts.readonlyRead contact data
contacts.writeCreate/update contacts
opportunities.writeManage pipeline deals
calendars.writeManage appointments
conversations.writeSend messages
forms.readonlyRead form submissions
workflows.writeEnroll contacts in workflows

Request only the scopes you need — minimal scope is a security best practice.


Core API Operations: Contacts

The contacts API is the most frequently used endpoint in GHL integrations.

Create or Update a Contact:

POST https://services.leadconnectorhq.com/contacts/
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json

{
  "firstName": "Jane",
  "lastName": "Smith",
  "email": "[email protected]",
  "phone": "+14155551234",
  "locationId": "YOUR_LOCATION_ID",
  "source": "shopify-integration",
  "tags": ["new-customer", "shopify"],
  "customFields": [
    {
      "id": "CUSTOM_FIELD_ID_FOR_ORDER_COUNT",
      "field_value": "1"
    }
  ]
}

Response (201 Created):

{
  "contact": {
    "id": "abc123xyz",
    "firstName": "Jane",
    "lastName": "Smith",
    "email": "[email protected]",
    "phone": "+14155551234",
    "locationId": "YOUR_LOCATION_ID",
    "tags": ["new-customer", "shopify"],
    "createdAt": "2026-03-19T10:30:00.000Z"
  }
}

Search for a Contact by Email (for deduplication):

GET https://services.leadconnectorhq.com/contacts/search
  ?locationId=YOUR_LOCATION_ID
  &[email protected]
Authorization: Bearer ACCESS_TOKEN

Always search before creating to avoid duplicate contact records.

Add a Tag to a Contact:

POST https://services.leadconnectorhq.com/contacts/abc123xyz/tags
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json

{
  "tags": ["vip-customer", "order-placed"]
}

Bulk Operations:

GHL supports bulk contact creation via the POST /contacts/bulk endpoint (verify in current API docs for your GHL version). For imports of more than 500 contacts, use the bulk endpoint with batches of 100 contacts per request to stay within rate limits.


Pipeline and Opportunity API

Create an Opportunity:

POST https://services.leadconnectorhq.com/opportunities/
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json

{
  "pipelineId": "YOUR_PIPELINE_ID",
  "pipelineStageId": "YOUR_STAGE_ID",
  "contactId": "abc123xyz",
  "name": "Jane Smith - HVAC Service",
  "monetaryValue": 450,
  "status": "open",
  "assignedTo": "USER_ID"
}

Move Opportunity to a New Stage:

PUT https://services.leadconnectorhq.com/opportunities/OPPORTUNITY_ID
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json

{
  "pipelineStageId": "NEW_STAGE_ID"
}

Common Integration Pattern: External CRM → GHL Pipeline Sync

When a deal is created in an external system (e.g., Salesforce), your integration code:

  1. Searches GHL for the contact by email
  2. Creates the contact if not found
  3. Creates a GHL opportunity linked to the contact
  4. Stores the GHL opportunity ID as a custom field in Salesforce for bidirectional sync

Inbound Webhooks: Triggering GHL from External Systems

Inbound webhooks let external systems fire GHL workflows. This is the primary mechanism for event-driven integration.

Creating an Inbound Webhook Trigger in GHL:

  1. Navigate to Automation > Workflows > New Workflow
  2. Select "Inbound Webhook" as the trigger type
  3. GHL generates a unique URL: https://services.leadconnectorhq.com/hooks/YOUR_UNIQUE_HOOK_ID/webhook-trigger
  4. Configure what data the workflow uses from the webhook payload

Sending Data to the Inbound Webhook:

POST https://services.leadconnectorhq.com/hooks/YOUR_UNIQUE_HOOK_ID/webhook-trigger
Content-Type: application/json

{
  "firstName": "John",
  "lastName": "Doe",
  "email": "[email protected]",
  "phone": "+14155559876",
  "customData": {
    "order_id": "ORD-12345",
    "product_name": "AC Tune-Up Service",
    "order_value": "299.00"
  }
}

GHL creates or updates a contact from the payload fields it recognizes (firstName, lastName, email, phone) and makes the customData fields available as variables in the workflow.

Use Cases for Inbound Webhooks:

  • eCommerce order placed → trigger post-purchase sequence
  • Support ticket created → add "active-support" tag, pause marketing sequences
  • Payment received → move pipeline to "Won," trigger onboarding workflow
  • Trial signup → start SaaS onboarding sequence
  • Form submitted on third-party site → capture lead into GHL CRM

Outbound Webhooks: GHL Notifying External Systems

Outbound webhooks send data from GHL to external systems when GHL events occur.

Configuring Outbound Webhooks in GHL:

Navigate to Settings > Integrations > Webhooks (sub-account level) or add a webhook action inside a workflow.

GHL Native Outbound Events (available in Settings > Webhooks):

  • Contact created
  • Contact updated
  • Contact deleted
  • Opportunity created/updated/deleted
  • Form submitted
  • Appointment booked/cancelled/no-showed
  • Conversation message (inbound)
  • Call started/ended

GHL Workflow Webhook Action:

For more granular control, add a "Send Webhook" action inside a workflow. This fires only when the workflow reaches that step, allowing you to control exactly which events notify external systems and what payload they receive.

// Example workflow webhook payload to your system
{
  "event": "appointment_booked",
  "contact": {
    "id": "{{contact.id}}",
    "name": "{{contact.full_name}}",
    "email": "{{contact.email}}",
    "phone": "{{contact.phone}}"
  },
  "appointment": {
    "calendar_id": "{{appointment.calendar_id}}",
    "start_time": "{{appointment.start_time}}",
    "service": "{{appointment.title}}"
  },
  "custom_fields": {
    "order_id": "{{contact.order_id}}"
  }
}

Use GHL's custom value syntax ({{variable.path}}) to include dynamic contact and event data in the payload.


Webhook Security: Signature Verification

GHL signs outbound webhooks with an HMAC-SHA256 signature. Your receiving endpoint should verify this signature to prevent spoofed requests.

Verification Process:

GHL includes a signature header with each webhook request:

X-GHL-Signature: sha256=COMPUTED_HMAC

Your server verifies:

const crypto = require('crypto');

function verifyGHLWebhook(payload, signature, secret) {
  const computedSignature = crypto
    .createHmac('sha256', secret)
    .update(payload) // raw request body as Buffer
    .digest('hex');

  const expectedSignature = `sha256=${computedSignature}`;

  return crypto.timingSafeEqual(
    Buffer.from(expectedSignature),
    Buffer.from(signature)
  );
}

Always use crypto.timingSafeEqual for comparison — string equality is vulnerable to timing attacks.

Your webhook secret is set when you create the webhook in GHL's settings.


Rate Limiting and Best Practices

GHL enforces rate limits on API access. As of 2026, the standard limit is approximately 100 requests per 10 seconds per location. Exceeding this returns a 429 Too Many Requests response.

Strategies for Staying Within Rate Limits:

1. Implement Exponential Backoff:

async function apiCallWithRetry(fn, maxRetries = 3) {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      if (error.status === 429 && attempt < maxRetries) {
        const delay = Math.pow(2, attempt) * 1000; // 1s, 2s, 4s
        await new Promise(r => setTimeout(r, delay));
      } else {
        throw error;
      }
    }
  }
}

2. Cache Contact Lookups: Don't search GHL by email on every incoming event. Cache contact ID lookups in Redis or your database with a 15-minute TTL. Most integration flows involve the same contacts repeatedly.

3. Batch Contact Updates: If you're updating custom fields for 500 contacts after a bulk data process, batch the updates in groups of 10 with a 100ms delay between batches rather than firing all 500 simultaneously.

4. Use Webhooks Instead of Polling: Never poll GHL's API for changes (e.g., checking every minute if new contacts were created). Use GHL's outbound webhooks to receive notifications when events occur. This eliminates polling-related rate limit consumption.


Building a GHL Marketplace App

If you're building an integration for multiple GHL customers, consider publishing it as a GHL Marketplace app. This allows GHL users to install your integration with a single click, using OAuth for authentication — no manual API key sharing required.

Requirements for Marketplace Listing:

  • OAuth 2.0 implementation
  • Privacy policy and terms of service URLs
  • App icon and description
  • Webhook handling for the events your app subscribes to
  • GHL review and approval process (typically 1–2 weeks)

Benefits of Marketplace Distribution:

  • One-click install for GHL users
  • OAuth handles authentication (no API key management)
  • Increased discoverability through GHL's marketplace
  • Ability to charge for the integration via GHL's billing infrastructure

This path is worth pursuing if you're building an integration that multiple GHL agencies or businesses would use — the distribution leverage is significant.


Common Integration Patterns

Pattern 1: eCommerce Order Sync

  • Shopify order webhook → your middleware → GHL contact update + tag + workflow enrollment
  • Middleware validates payload, deduplicates contacts, maps order data to GHL custom fields

Pattern 2: ERP to CRM Bridge

  • ERP (Odoo, QuickBooks) invoice created → webhook to middleware → GHL opportunity marked Won + pipeline moved
  • Two-way sync: GHL pipeline change → ERP order status update

Pattern 3: Appointment + Field Service Sync

  • GHL appointment booked → outbound webhook → FSM tool creates job
  • FSM job completed → webhook to GHL → move pipeline to Completed + trigger review sequence

Pattern 4: Data Warehouse Reporting

  • Daily: GHL API pulls previous day's contacts, opportunities, and communication events
  • Data stored in your data warehouse (BigQuery, Snowflake)
  • Power BI or Looker connects to data warehouse for advanced cross-platform analytics

Frequently Asked Questions

What's the difference between GHL's v1 and v2 API?

GHL's v2 API (introduced around 2022–2023) uses OAuth 2.0 and a cleaner REST design compared to v1's API key authentication. The v2 API has more comprehensive endpoint coverage and better documentation. New integrations should be built on v2. GHL has stated intent to deprecate v1 but has not yet set a firm timeline — check GHL's developer announcements for the current status.

Can I use GHL's API to send SMS messages programmatically?

Yes. Use the POST /conversations/messages endpoint to send SMS messages to a contact. You need the conversation ID (created by POST /conversations/) and the contact's phone number. Ensure the contact has SMS opt-in status before sending — GHL enforces this, and sending to opted-out contacts will fail. Include the required type: "SMS" parameter and your GHL location's Twilio number as the sender.

How do I handle GHL API pagination for large datasets?

GHL's list endpoints return paginated results. The response includes a meta object with total, currentPage, and nextPage (or cursor-based startAfterId). Implement pagination by iterating through pages until nextPage is null or you've collected all records. For large contact exports (100,000+ contacts), use GHL's bulk export feature or contact GHL support to request a data export — paginating through the API for very large datasets is slow and rate-limit-intensive.

Is there a sandbox environment for testing GHL API integrations?

GHL does not have a dedicated sandbox environment. Use a separate GHL trial account or a development sub-account for testing. Create test contacts with clearly labeled emails (e.g., [email protected]) to distinguish test data from real contacts. Clean up test data regularly to keep your development account organized.

What's the best way to store GHL contact IDs in my external system?

Store the GHL contact.id (a unique string) as a custom field in your external system (e.g., a ghl_contact_id column in your database). This enables direct API calls to the correct contact without a search step. When creating contacts in GHL, store the returned ID immediately. For bidirectional sync, also store your system's unique ID as a GHL custom field (e.g., external_user_id) for the reverse lookup.


Next Steps

GoHighLevel's API and webhook system make it a genuinely extensible platform — not just a no-code marketing tool, but a programmable customer communication engine that can integrate with virtually any business system. The key is building clean, well-tested integrations with proper error handling and signature verification from the start.

ECOSIRE's GoHighLevel services include custom API integration development. Our technical team builds GHL integrations for eCommerce platforms, ERP systems, field service management tools, and proprietary business applications. We design integrations with proper error handling, rate limit management, and monitoring.

Contact our team to discuss your custom integration requirements and get a technical scope for your specific GHL integration project.

E

Written by

ECOSIRE Research and Development Team

Building enterprise-grade digital products at ECOSIRE. Sharing insights on Odoo integrations, e-commerce automation, and AI-powered business solutions.

Chat on WhatsApp