Install
$ agentstack add skill-srana-git-claude-code-ghl-skill-claude-code-ghl-skill ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
GHL API Skill
Comprehensive GoHighLevel API access for multiple subaccounts.
Quick Start
1. Add Your First Account
Add my GHL account called "my_agency"
You'll be prompted for:
- Location ID (from GHL Settings → Business Profile)
- Private Integration Token (from Settings → Integrations → Private Integrations)
2. Test Connection
Test connection to my_agency account
3. Start Using
List contacts in my_agency
Search contacts for "john@example.com" in my_agency
Send SMS to contact xyz in my_agency: "Thanks for your interest!"
Workflow Routing
Account Selection
IF no account specified AND only one account configured: → Use default account IF no account specified AND multiple accounts: → Ask "Which GHL account?" + list available names IF account name provided: → Load that account's credentials from config
Operation Routing
Contact Operations:
- "get contact", "find contact", "search contacts", "create contact", "update contact", "delete contact"
- "add tags to contact", "remove tags", "add note", "list tasks"
→ Script: python3 ghl-api/scripts/contacts.py [args]
Custom Fields:
- "get custom fields", "list custom fields", "create custom field", "update custom field value"
→ Script: python3 ghl-api/scripts/custom_fields.py [args]
Custom Objects:
- "list schemas", "create schema", "list records", "create record", "search records"
→ Script: python3 ghl-api/scripts/custom_objects.py [args]
Conversations & Messaging:
- "send sms", "send email", "send message", "list conversations", "get messages", "search conversations"
→ Script: python3 ghl-api/scripts/conversations.py [args]
Email & Email Builder Templates:
- "send email", "send email to contact", "send template to contact"
- "list builder templates", "create builder template", "update template html", "update template metadata"
→ Script: python3 ghl-api/scripts/emails.py [args]
Social Planner:
- "list social posts", "create social post", "schedule post", "list social accounts"
→ Script: python3 ghl-api/scripts/social_planner.py [args]
Forms & Surveys:
- "list forms", "get form submissions", "list surveys", "get survey responses"
→ Scripts: forms.py or surveys.py
Workflows:
- "list workflows", "trigger workflow for contact", "remove from workflow"
→ Script: python3 ghl-api/scripts/workflows.py [args]
Opportunities & Pipelines:
- "list pipelines", "list opportunities", "create opportunity", "move to stage", "mark won/lost"
→ Script: python3 ghl-api/scripts/opportunities.py [args]
Invoices & Payments:
- "list invoices", "create invoice", "send invoice", "record payment"
- "list orders", "list transactions", "list subscriptions", "manage coupons"
→ Scripts: invoices.py or payments.py
> ⚠️ Invoice API Gotcha: Creating invoices requires businessDetails (with nested > address object) and contactDetails (with id field) — these are NOT optional > despite GHL docs suggesting otherwise. Items use amount (not price) and need > currency per item. See "Invoice API Requirements" section below for full details.
Products:
- "list products", "create product", "add price", "manage collections"
→ Script: python3 ghl-api/scripts/products.py [args]
AI Features:
- "list voice agents", "create voice agent", "get call logs", "list chat bots"
- "manage knowledge base", "crawl website into KB", "add FAQ to KB"
→ Scripts: voice_ai.py, conversation_ai.py, knowledge_base.py, agent_studio.py
Infrastructure:
- "get location info", "list tags", "upload media", "list phone numbers"
→ Scripts: subaccounts.py, media.py, phone_system.py, proposals.py
Account Management:
- "add ghl account", "list ghl accounts", "remove ghl account", "test ghl account"
→ Script: python3 ghl-api/scripts/accounts.py [args]
Natural Language Examples
Contacts
Get all contacts tagged "hot-lead" from my_agency
Create a contact in my_agency: email john@example.com, name John Doe, phone 555-1234
Update contact xyz in my_agency: set company to "Acme Corp"
Add tags "vip,priority" to contact xyz in my_agency
Search contacts in my_agency for anyone with email containing "@gmail.com"
Messaging
Send SMS to contact xyz in my_agency: "Your appointment is confirmed for tomorrow"
Send email to contact xyz in my_agency with subject "Welcome!" and body "Thanks for signing up"
List recent conversations in my_agency
Get messages from conversation abc in my_agency
Workflows
List all workflows in my_agency
Trigger workflow abc for contact xyz in my_agency
Remove contact xyz from workflow abc in my_agency
Opportunities
List all pipelines in my_agency
Create opportunity "Big Deal" for contact xyz in pipeline abc, stage def, value $10000
Move opportunity xyz to stage "Proposal Sent" in my_agency
Mark opportunity xyz as won in my_agency
AI Features
List all voice AI agents in my_agency
Get call logs for agent xyz in my_agency
List knowledge bases in my_agency
Crawl https://example.com/docs into knowledge base xyz in my_agency
Check crawl status for operation abc in KB xyz
Train discovered pages into KB xyz
List trained URLs in knowledge base xyz
Add FAQ to KB xyz: question "What is your return policy?" answer "30-day money back"
List FAQs in knowledge base xyz
Email Builder Templates
# List all builder templates
python3 emails.py my_agency list-builder-templates --limit 10
# Create a new builder template (returns ID)
python3 emails.py my_agency create-builder-template --type html
# Update template HTML content
python3 emails.py my_agency update-builder-html --html "..."
# Update from HTML file
python3 emails.py my_agency update-builder-html --html-file /path/to/email.html
# Get template preview URL
python3 emails.py my_agency get-builder-preview
Email Builder API (Updated Mar 2026)
| Action | API Support | Notes | |--------|-------------|-------| | List builder templates | ✅ Works | Returns name, ID, preview URL | | Create builder template | ✅ Works | Creates as "New Template" | | Update HTML content | ✅ Works | Uses POST /emails/builder/data endpoint | | Set template name | ✅ Works | Uses PATCH /emails/builder/:id | | Set subject line | ✅ Works | Uses PATCH /emails/builder/:id | | Set from name/email | ✅ Works | Uses PATCH /emails/builder/:id | | Set preview text | ✅ Works | Uses PATCH /emails/builder/:id | | Delete template | ❌ No | Must use GHL UI | | Duplicate template | ❌ No | Can't read full template content | | Regular templates | ❌ No | /emails/templates returns 404 |
Recommended Workflow
- Create template via API:
``bash python3 emails.py my_agency create-builder-template --type html # Returns: ID: abc123xyz ``
- Set metadata via API:
``bash python3 emails.py my_agency update-builder-metadata abc123xyz \ --name "My Newsletter" \ --subject "Weekly Update" \ --from-name "My Agency" \ --from-email "newsletter@myagency.com" \ --preview-text "This week's top updates..." ``
- Update HTML content via API:
``bash python3 emails.py my_agency update-builder-html abc123xyz --html-file my-email.html ``
- Future updates — both content and metadata can be updated anytime via API.
Invoice API Requirements (Feb 2026)
The GHL Invoice Create endpoint (POST /invoices/) has undocumented required fields that will cause a 500: Cannot read properties of undefined (reading 'toString') error if missing.
Required Payload Structure
{
"altId": "",
"altType": "location",
"contactId": "",
"name": "Invoice name (internal)",
"currency": "USD",
"issueDate": "2026-02-05",
"businessDetails": {
"name": "Your Business Name",
"address": {
"addressLine1": "123 Main St",
"city": "Austin",
"state": "Texas",
"countryCode": "US",
"postalCode": "73301"
}
},
"contactDetails": {
"id": "",
"name": "Client Name",
"email": "client@example.com",
"phoneNo": "+11234567890"
},
"items": [
{
"name": "Line item description",
"description": "Optional details",
"currency": "USD",
"amount": 976.58,
"qty": 1
}
],
"sendInvoice": false
}
Field Reference
| Field | Required | Notes | |-------|----------|-------| | altId | Yes | Location ID | | altType | Yes | Always "location" | | contactId | Yes | GHL Contact ID | | issueDate | Yes | YYYY-MM-DD format | | businessDetails | Yes | Will 500 error if missing | | businessDetails.name | Yes | Your business name | | businessDetails.address | Yes | Must be an object (not a string) | | businessDetails.address.addressLine1 | Yes | Street address | | businessDetails.address.city | Yes | City | | businessDetails.address.state | Yes | State/Province | | businessDetails.address.countryCode | Yes | 2-letter code (CA, US) | | businessDetails.address.postalCode | Yes | Postal/ZIP code | | contactDetails | Yes | Will 500 error if missing | | contactDetails.id | Yes | Must match contactId | | contactDetails.name | Yes | Display name on invoice | | contactDetails.email | No | Client email | | contactDetails.phoneNo | No | Client phone | | items[].name | Yes | Line item name | | items[].amount | Yes | Use amount, NOT price | | items[].qty | Yes | Min value: 0.1 | | items[].currency | Yes | Per-item currency code | | items[].description | No | Additional details | | currency | No | Top-level currency (USD, CAD, etc.) | | name | No | Internal invoice name | | title | No | Displayed title on invoice | | dueDate | No | YYYY-MM-DD — defaults to ~13 days after issue | | termsNotes | No | Use this for terms/notes (single string field). notes and terms are silently ignored! | | discount.type | No | "percentage" or "fixed" | | discount.value | No | Discount amount | | sendInvoice | No | true to email immediately, default false (draft) |
Common Pitfalls
| Mistake | Error | Fix | |---------|-------|-----| | Missing businessDetails | 500: toString undefined | Add full businessDetails with nested address object | | Missing contactDetails | 500: toString undefined | Add contactDetails with id field | | businessDetails.address as string | 422: must be object or array | Use nested object with addressLine1, city, etc. | | Missing contactDetails.id | 422: id should not be empty | Set to same value as contactId | | Using price in items | 500: toString undefined | Use amount instead | | Using quantity in items | 422: qty should not be empty | Use qty instead | | Missing issueDate | 422: issueDate should not be empty | Add in YYYY-MM-DD format | | Missing items[].currency | May cause issues | Always include per-item currency | | Using notes or terms | Silently ignored | Use termsNotes (single string) instead |
API Response (201 Created)
A successful response includes:
_id— Invoice ID (use for send, void, record-payment)invoiceNumber— Auto-generated sequence numberstatus—"draft"whensendInvoice: falsetotal/amountDue— Calculated totalsdueDate— Auto-calculated if not specified (~13 days default)
Configuration
Accounts File
Location: ghl-api/config/accounts.json
{
"my_agency": {
"location_id": "your_location_id_here",
"token_env_var": "GHL_TOKEN_MY_AGENCY"
},
"client_account": {
"location_id": "another_location_id",
"token_env_var": "GHL_TOKEN_CLIENT"
}
}
Tokens File
Location: ghl-api/.env
GHL_TOKEN_MY_AGENCY=pit_xxxxxxxxxxxx
GHL_TOKEN_CLIENT=pit_yyyyyyyyyyyy
Error Handling
The skill provides clear error messages:
❌ Auth Error: Token for account 'my_agency' is invalid or expired
→ Run: python3 accounts.py update my_agency --token
❌ Rate Limit: Too many requests. Waiting 10s then retrying...
✅ Retry successful
❌ Not Found: Contact 'abc123' not found in account 'my_agency'
❌ Validation Error: 'email' field is required for contact creation
API Coverage
Core CRM (Priority 1)
- ✅ Contacts - Full CRUD, search, tags, notes, tasks
- ✅ Custom Fields - Field definitions and values
- ✅ Custom Objects - Schemas and records
- ✅ Conversations - Messages, threads, recordings
- ✅ Emails - Send, templates, builder (metadata + HTML)
- ✅ Social Planner - Posts, accounts, scheduling
Marketing & Automation (Priority 2)
- ✅ Forms - List and submissions
- ✅ Surveys - List and submissions
- ✅ Workflows - List and trigger
- ✅ Trigger Links - CRUD
- ✅ Funnels - Pages and redirects
- ✅ Blogs - Posts, categories, authors
Sales & Payments (Priority 3)
- ✅ Opportunities - Pipelines, stages, CRUD
- ✅ Invoices - CRUD, templates, schedules
- ✅ Payments - Orders, transactions, subscriptions
- ✅ Products - Catalog, prices, collections
AI Features (Priority 4)
- ✅ Voice AI - Agents, call logs, goals
- ✅ Conversation AI - Chat bots, generations
- ✅ Knowledge Base - Documents, search
- ✅ Agent Studio - AI agent configuration
Infrastructure (Priority 5)
- ✅ Subaccounts/Locations - Settings, tags, templates
- ✅ Media - File storage
- ✅ Phone System - Numbers, pools
- ✅ Proposals - Documents, contracts
Rate Limits
GHL API limits:
- 100 requests per 10 seconds
- 200,000 requests per day
The skill automatically handles rate limits with exponential backoff.
Module Reference
| Module | Purpose | Key Operations | |--------|---------|----------------| | accounts.py | Account management | add, list, test, remove | | contacts.py | Contact operations | CRUD, search, tags, notes | | custom_fields.py | Custom fields | CRUD field definitions | | custom_objects.py | Custom objects | Schemas, records | | conversations.py | Messaging | SMS, email, threads | | emails.py | Email operations | Send, builder templates, HTML + metadata updates | | forms.py | Form data | List, submissions | | surveys.py | Survey data | List, submissions | | workflows.py | Automation | List, trigger | | opportunities.py | Pipeline/deals | CRUD, stages | | invoices.py | Billing | CRUD, send, payments | | payments.py | Transactions | Orders, subscriptions | | products.py | Product catalog | CRUD, prices | | voice_ai.py | Voice agents | CRUD, call logs | | conversation_ai.py | Chat bots | CRUD, generations | | knowledge_base.py | KB management | CRUD, web crawler, FAQs | | agent_studio.py | AI agents | Configuration | | subaccounts.py | Location settings | Tags, templates | | media.py | File storage | Upload, folders | | phone_system.py | Phone numbers | CRUD, pools | | proposals.py | Documents | Contracts | | social_planner.py | Social media | Posts CRUD, accounts, scheduling |
Troubleshooting
"Account not found"
# Check configured accounts
python3 ghl-api/scripts/accounts.py list
"Token invalid or expired"
- Generate new token in GHL: Settings → Integrations → Private Integrations
- Update the skill:
python3 ghl-api/scripts/accounts.py update my_agency --token pit_new_token
"Rate limit exceeded"
The skill auto-retries on rate limits. If persistent:
- Reduce request frequency
- Check daily quota in GHL
Testing a specific module
cd ghl-api/scripts
python3 contacts.py my_agency list --limit 5
python3 workflows.py my_agency list
python3 conversations.py my_agency list --limit 5
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: srana-git
- Source: srana-git/claude-code-ghl-skill
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.