Object Schema CRUD Setup
Define custom object schemas to extend your GHL CRM beyond standard contacts and opportunities. This guide walks you through creating, reading, updating, and listing object schemas via the API.
Prerequisites
Before working with custom object schemas, confirm you have:
- A GoHighLevel sub-account on a plan that supports Custom Objects
- Access to Settings > Integrations > Private Integrations in your sub-account
- Your
locationId(found in Settings > Business Profile) - A REST client such as Postman, Insomnia, or
curlin your terminal - A clear plan for the object you want to model (field names, types, validation rules)
Set Up Authentication
All Custom Object endpoints require a Private Integration Token (PIT) with the custom-objects scope.
- Navigate to Settings > Integrations > Private Integrations
- Click Create Private Integration and name it “Custom Objects Manager”
- Under Scopes, enable
custom-objectswith read and write permissions - Click Save and copy the generated token
Set environment variables for convenience:
export GHL_TOKEN="your-private-integration-token"
export LOCATION_ID="your-location-id"
Every request uses https://services.leadconnectorhq.com as the base URL. Include these headers on all calls:
Authorization: Bearer $GHL_TOKEN
Content-Type: application/json
Version: 2021-07-28
Make Your First Call
Start by creating a custom object schema. Send a POST request to /objects with your schema definition:
curl -X POST "https://services.leadconnectorhq.com/objects" \
-H "Authorization: Bearer $GHL_TOKEN" \
-H "Content-Type: application/json" \
-H "Version: 2021-07-28" \
-d '{
"locationId": "'"$LOCATION_ID"'",
"label": "Property",
"key": "property",
"description": "Real estate property records",
"fields": [
{"key": "address", "label": "Address", "type": "TEXT", "required": true},
{"key": "price", "label": "Asking Price", "type": "NUMERICAL"},
{"key": "status", "label": "Listing Status", "type": "SINGLE_OPTIONS", "options": ["Active", "Pending", "Sold"]}
]
}'
Each field definition accepts key, label, type, required, and type-specific options. Supported types include TEXT, NUMERICAL, SINGLE_OPTIONS, MULTIPLE_OPTIONS, DATE, and PHONE.
To list all schemas, use GET /objects?locationId={locationId}. To retrieve a single schema, use GET /objects/{objectId}.
Handle the Response
A successful POST /objects returns a 200 status with the full schema object:
{
"object": {
"id": "obj_abc123",
"locationId": "your-location-id",
"key": "property",
"label": "Property",
"fields": [...],
"createdAt": "2026-03-07T12:00:00Z"
}
}
Save the object.id value. You need it for updates with PUT /objects/{id} and for creating records within this object. The key field is also important, as other endpoints reference objects by key.
Common errors: 400 means your schema definition is malformed or a required property is missing. 401 indicates an invalid token or missing scope. 409 means an object with that key already exists in the location.
Test Your Setup
Run through this checklist to verify your integration:
- Create a test schema with
POST /objectsand confirm the response includes anid - List all schemas with
GET /objects?locationId={id}and verify your new object appears - Read the schema by ID with
GET /objects/{objectId}to see the full definition - Update the schema with
PUT /objects/{objectId}, adding a new field to thefieldsarray - Verify the update by reading the schema again and confirming the new field exists
If you get 401, regenerate your PIT and confirm the custom-objects scope is enabled. If 409 appears on create, your object key is already in use. Choose a unique key or retrieve the existing schema.
Next Steps
Read the full Object Schema CRUD guide for advanced patterns including field validation rules and display configuration. Once your schema is defined, set up Object Records to start creating data within it, or explore Associations to link your custom object to contacts and other built-in objects.