Opportunity Upsert Setup
Use the upsert endpoint to create or update opportunities in a single API call. The system resolves existing records by contact and pipeline, which eliminates duplicate opportunities and simplifies your integration logic.
Prerequisites
Before you begin, confirm the following:
- A GoHighLevel sub-account with at least one pipeline configured
- A Private Integration Token (PIT) with the opportunities scope enabled
- Your
locationIdfrom Settings > Business Info - Valid
pipelineIdandpipelineStageIdvalues (use the Pipeline Getter endpoint if needed) - At least one contact in your CRM for testing
Set Up Authentication
The upsert endpoint uses standard Bearer token authentication.
- Navigate to Settings > Integrations > Private Integrations
- Create or select a PIT with opportunities scope (read and write)
- Copy the token and configure your environment:
export GHL_TOKEN="your-private-integration-token"
export LOCATION_ID="your-location-id"
Include these headers on every request:
Authorization: Bearer $GHL_TOKEN
Content-Type: application/json
Version: 2021-07-28
Make Your First Call
The upsert endpoint is POST /opportunities/upsert. It checks if an opportunity already exists for the given contact and pipeline combination. If it finds one, it updates it. If not, it creates a new one.
Upsert an opportunity:
curl -X POST "https://services.leadconnectorhq.com/opportunities/upsert" \
-H "Authorization: Bearer $GHL_TOKEN" \
-H "Content-Type: application/json" \
-H "Version: 2021-07-28" \
-d '{
"pipelineId": "your-pipeline-id",
"pipelineStageId": "your-stage-id",
"locationId": "'"$LOCATION_ID"'",
"contactId": "your-contact-id",
"name": "Website Redesign Project",
"monetaryValue": 12000,
"status": "open"
}'
Run the same call again with a different monetaryValue or pipelineStageId. The API updates the existing opportunity instead of creating a duplicate:
curl -X POST "https://services.leadconnectorhq.com/opportunities/upsert" \
-H "Authorization: Bearer $GHL_TOKEN" \
-H "Content-Type: application/json" \
-H "Version: 2021-07-28" \
-d '{
"pipelineId": "your-pipeline-id",
"pipelineStageId": "next-stage-id",
"locationId": "'"$LOCATION_ID"'",
"contactId": "your-contact-id",
"name": "Website Redesign Project",
"monetaryValue": 15000,
"status": "open"
}'
The resolution logic matches on contactId plus pipelineId. If you use the same contact but a different pipeline, the system creates a new opportunity in that second pipeline.
Handle the Response
A successful response returns 200 with the opportunity object and a new boolean field:
new: true— A new opportunity was created. The response includes the full object with the generatedid.new: false— An existing opportunity was found and updated. The response includes the updated object.
The returned object contains id, name, monetaryValue, pipelineId, pipelineStageId, contactId, status, and any custom fields you passed.
Common errors:
- 401 — Invalid or expired token. Check your PIT.
- 404 — Pipeline or stage ID not found. Use the Pipeline Getter to verify valid IDs.
- 422 — Missing required fields. The upsert requires
pipelineId,locationId, andcontactIdat minimum.
Test Your Setup
Follow these steps to confirm the upsert behaves correctly:
- Send the upsert call with a contact who has no existing opportunity in the pipeline. Confirm
new: truein the response. - Send the same call again with an updated
monetaryValue. Confirmnew: falseand the value changed. - Send the call with the same contact but a different
pipelineId. Confirmnew: true(separate pipeline, separate opportunity). - Check the GHL dashboard under Opportunities to verify only one record exists per contact per pipeline.
- Delete your test opportunities using the CRUD endpoint when finished.
If the upsert creates duplicates, verify you are using the exact same contactId and pipelineId in both calls.
Next Steps
Read the full Opportunity Upsert guide for advanced patterns including custom field mapping and webhook triggers. Pair this with the Status Manager to automate stage transitions after upsert, or use Opportunity Search to verify records before upserting.