📇

GoHighLevel CRM

Verified

by OpenClaw Launch

Integrate with GoHighLevel CRM to create and manage contacts, send SMS and email messages, track deals through pipelines, and book appointments via the GHL API v2. Designed for service businesses that use GoHighLevel as their CRM.

gohighlevelghlcrmcontactspipelinecalendarsmsappointmentshighlevel

GoHighLevel CRM

Integrate with GoHighLevel to manage contacts, send messages (SMS/email), track deals through pipelines, and book appointments. Uses the GHL API v2 with a Private Integration Token.

Setup

You need two values from your GoHighLevel account:

  1. API Token -- Go to Settings > Business Profile > Integrations > Private Integration Token. Copy the token.
  2. Location ID -- Go to Settings > Business Info. Your Location ID is in the URL: https://app.gohighlevel.com/v2/location/YOUR_LOCATION_ID/...

Tell the bot: "My GHL API token is TOKEN and my location ID is LOCATION_ID"

The bot will store these securely and use them for all GHL API calls.

API Reference

Base URL: https://services.leadconnectorhq.com

Required headers for every request:

Authorization: Bearer GHL_API_TOKEN
Version: 2021-07-28
Content-Type: application/json

Note: Calendar endpoints use Version: 2021-04-15 instead.

Contacts

Create a contact:

curl -X POST https://services.leadconnectorhq.com/contacts/ \
  -H "Authorization: Bearer $GHL_TOKEN" \
  -H "Version: 2021-07-28" \
  -H "Content-Type: application/json" \
  -d '{"firstName":"John","lastName":"Doe","email":"[email protected]","phone":"+1234567890","locationId":"LOC_ID","tags":["lead"],"source":"OpenClaw Bot"}'

Upsert contact (create or update by email/phone -- avoids duplicates):

POST /contacts/upsert

Same body as create. Use this instead of create when you are unsure if the contact already exists.

Search contacts:

GET /contacts/?locationId=LOC_ID&[email protected]
GET /contacts/?locationId=LOC_ID&phone=+1234567890

Update contact:

PUT /contacts/{contactId}

Body: partial fields only, e.g. {"tags":["qualified"]}

Add/remove tags:

POST   /contacts/{contactId}/tags   body: {"tags":["tag1","tag2"]}
DELETE /contacts/{contactId}/tags   body: {"tags":["tag1"]}

Add contact to workflow:

POST /contacts/{contactId}/workflow/{workflowId}

Conversations & Messaging

Send SMS:

curl -X POST https://services.leadconnectorhq.com/conversations/messages \
  -H "Authorization: Bearer $GHL_TOKEN" \
  -H "Version: 2021-07-28" \
  -H "Content-Type: application/json" \
  -d '{"type":"SMS","contactId":"CONTACT_ID","message":"Your appointment is confirmed for tomorrow at 10am."}'

Send email:

POST /conversations/messages
body: {"type":"Email","contactId":"CONTACT_ID","subject":"Appointment Confirmation","html":"<p>Your appointment is confirmed.</p>"}

Message types: SMS, Email, WhatsApp, FB, IG, Live_Chat, GMB

Get conversation messages:

GET /conversations/{conversationId}/messages

Opportunities / Pipeline

First, get your pipeline and stage IDs:

GET /opportunities/pipelines?locationId=LOC_ID

This returns all pipelines with their stages. Save the pipeline ID and stage IDs.

Create opportunity (deal):

curl -X POST https://services.leadconnectorhq.com/opportunities/ \
  -H "Authorization: Bearer $GHL_TOKEN" \
  -H "Version: 2021-07-28" \
  -H "Content-Type: application/json" \
  -d '{"title":"Cleaning - 123 Main St","pipelineId":"PIPELINE_ID","stageId":"STAGE_ID","contactId":"CONTACT_ID","locationId":"LOC_ID","monetaryValue":350,"status":"open"}'

Update opportunity stage (move deal forward):

PUT /opportunities/{opportunityId}
body: {"stageId":"NEW_STAGE_ID"}

Mark deal as won/lost:

PUT /opportunities/{opportunityId}/status
body: {"status":"won"}

Status values: open, won, lost, abandoned

Search opportunities:

GET /opportunities/search?location_id=LOC_ID&pipeline_id=PIPELINE_ID

Calendars & Appointments

IMPORTANT: Calendar endpoints use Version: 2021-04-15

Get all calendars:

GET /calendars/?locationId=LOC_ID

Check available time slots:

GET /calendars/{calendarId}/free-slots?startDate=1713600000000&endDate=1714204800000&timezone=America/New_York

Dates are Unix timestamps in milliseconds.

Book an appointment:

curl -X POST https://services.leadconnectorhq.com/calendars/events/appointments \
  -H "Authorization: Bearer $GHL_TOKEN" \
  -H "Version: 2021-04-15" \
  -H "Content-Type: application/json" \
  -d '{"calendarId":"CAL_ID","locationId":"LOC_ID","contactId":"CONTACT_ID","startTime":"2026-04-20T14:00:00Z","endTime":"2026-04-20T15:00:00Z","title":"Cleaning Service","appointmentStatus":"confirmed","address":"123 Main St","toNotify":true}'

Update appointment:

PUT /calendars/events/appointments/{eventId}

Cancel appointment:

PUT /calendars/events/appointments/{eventId}
body: {"appointmentStatus":"cancelled"}

Examples

  • "A new client just called asking for a house cleaning quote at 123 Main St. Add them to GoHighLevel and create a deal in my pipeline."
  • "Check my calendar availability for next Tuesday and book a painting estimate for the client we just spoke with."
  • "Send an SMS to the client confirming their appointment tomorrow at 2pm."
  • "Move the cleaning job for John Doe to the 'Completed' stage and mark it as won."

Guidelines

  • Always use /contacts/upsert instead of /contacts/ to avoid creating duplicate contacts
  • Before creating an opportunity, call GET /opportunities/pipelines to get valid pipeline and stage IDs
  • Before booking an appointment, call GET /calendars/{calendarId}/free-slots to verify the slot is available
  • Use the contact's phone number as the primary identifier when upserting (most reliable for service businesses)
  • Store the GHL API token and location ID as environment variables, never hardcode them or include them in conversation logs
  • If a request returns 429 (rate limited), wait and retry with exponential backoff
  • If a request returns 401, the token may be invalid -- ask the user to check their Private Integration Token in GHL settings
  • Always include "source": "OpenClaw Bot" when creating contacts so the user can track bot-generated leads in GHL
  • Calendar endpoints require Version: 2021-04-15 header, all other endpoints use Version: 2021-07-28
  • When the user asks to "send a message", default to SMS unless they specify email or another channel