# Ghl Api

> |

- **Type:** Skill
- **Install:** `agentstack add skill-srana-git-claude-code-ghl-skill-claude-code-ghl-skill`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [srana-git](https://agentstack.voostack.com/s/srana-git)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [srana-git](https://github.com/srana-git)
- **Source:** https://github.com/srana-git/claude-code-ghl-skill

## Install

```sh
agentstack add skill-srana-git-claude-code-ghl-skill-claude-code-ghl-skill
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## 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

1. **Create template via API:**
   ```bash
   python3 emails.py my_agency create-builder-template --type html
   # Returns: ID: abc123xyz
   ```

2. **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..."
   ```

3. **Update HTML content via API:**
   ```bash
   python3 emails.py my_agency update-builder-html abc123xyz --html-file my-email.html
   ```

4. **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

```json
{
  "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 number
- `status` — `"draft"` when `sendInvoice: false`
- `total` / `amountDue` — Calculated totals
- `dueDate` — Auto-calculated if not specified (~13 days default)

---

## Configuration

### Accounts File
Location: `ghl-api/config/accounts.json`

```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`

```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"
```bash
# Check configured accounts
python3 ghl-api/scripts/accounts.py list
```

### "Token invalid or expired"
1. Generate new token in GHL: Settings → Integrations → Private Integrations
2. Update the skill:
```bash
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
```bash
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](https://github.com/srana-git)
- **Source:** [srana-git/claude-code-ghl-skill](https://github.com/srana-git/claude-code-ghl-skill)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** yes
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-srana-git-claude-code-ghl-skill-claude-code-ghl-skill
- Seller: https://agentstack.voostack.com/s/srana-git
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
