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:
- API Token -- Go to Settings > Business Profile > Integrations > Private Integration Token. Copy the token.
- 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