Skip to main content
UPS
Step-by-Step Setup 10-15 minutes

Opportunity Upsert Setup

Pipelines & Opportunities Intermediate
Need more detail? Read the full guide for config deep-dives and best practices.

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 locationId from Settings > Business Info
  • Valid pipelineId and pipelineStageId values (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.

  1. Navigate to Settings > Integrations > Private Integrations
  2. Create or select a PIT with opportunities scope (read and write)
  3. 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 generated id.
  • 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, and contactId at minimum.

Test Your Setup

Follow these steps to confirm the upsert behaves correctly:

  1. Send the upsert call with a contact who has no existing opportunity in the pipeline. Confirm new: true in the response.
  2. Send the same call again with an updated monetaryValue. Confirm new: false and the value changed.
  3. Send the call with the same contact but a different pipelineId. Confirm new: true (separate pipeline, separate opportunity).
  4. Check the GHL dashboard under Opportunities to verify only one record exists per contact per pipeline.
  5. 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.

Stay sharp. New guides and playbooks as they drop.