Skip to content
RoughFigure

How to send leads to GoHighLevel through the API

Send website leads into a GoHighLevel sub-account with a Private Integration token and the contacts upsert endpoint, without making duplicate contacts.

RoughFigure4 min read
Illustration of a contact card in a CRM
On this page
  1. What you need before you start
  2. The request
  3. Duplicates: how upsert decides
  4. Handling errors and retries
  5. Starting a workflow when the lead arrives
  6. Doing it in RoughFigure
  7. Questions

To send leads to GoHighLevel (HighLevel) through the API, create a Private Integration token in the sub-account with the contacts.write scope, then POST each lead to https://services.leadconnectorhq.com/contacts/upsert with the sub-account’s Location ID. Upsert adds the contact if they’re new and updates them if they already exist, so a customer who enquires twice doesn’t become two contacts.

That’s the short version. Below is each step, the exact request, and the mistakes that trip people up.

What you need before you start

  • Access to the sub-account (the location) that leads should land in. Agency-level keys aren’t needed for this.
  • A Private Integration token. In the sub-account, open Settings → Private Integrations, choose Create new Integration, name it after your website or estimator, and tick only the contacts.write scope. Copy the token straight away: HighLevel won’t show it again.
  • The Location ID, from the sub-account’s Settings → Business Profile.

Give the token the fewest scopes that do the job. A token that can only write contacts can’t read your conversations, payments or calendars if it ever leaks. There’s more on this in how to keep CRM API keys safe.

The request

POST https://services.leadconnectorhq.com/contacts/upsert
Authorization: Bearer <your Private Integration token>
Version: v3
Content-Type: application/json

{
  "locationId": "<your Location ID>",
  "firstName": "Sophie",
  "lastName": "Turner",
  "email": "[email protected]",
  "phone": "+447700900123",
  "postalCode": "BS6 5QA",
  "source": "Website estimator",
  "tags": ["website-lead", "fencing"]
}

A few details matter:

  • The Version header is required. Without it the request is refused. The current contacts upsert reference lists v3; check the version switcher in HighLevel’s API docs if you’re maintaining older code that uses a date version.
  • locationId is the only required field, but send an email or phone, or there’s nothing to match a returning customer on.
  • Phone numbers work best in international format (+44…, +1…), so HighLevel’s SMS and calls work without editing.
  • source and tags are what let you build a workflow that fires only for website leads.

The reply tells you whether the contact was new:

{ "new": true, "contact": { "id": "…", "locationId": "…" }, "traceId": "…" }

Keep the contact id if you want to add notes or opportunities to the same person later.

Duplicates: how upsert decides

Upsert follows the sub-account’s Allow Duplicate Contact setting. With duplicates off (the usual choice), HighLevel matches on email and phone and updates the person it finds. If your sub-account allows duplicates, every enquiry can make a new contact. Why duplicates happen and how upsert stops them goes into this in more depth.

Handling errors and retries

Reply What it means What to do
401 The token is wrong, revoked or rotated Make a new token and update it
403 The token is missing contacts.write or is for another location Check scopes and the Location ID
422 A field is invalid, often a badly formatted phone or email Fix the data, don’t retry as-is
429 or 5xx Rate limit or a HighLevel outage Retry later, with a growing wait

Never drop a lead because the CRM was down. Save it first, then send it, and retry for a few hours on 429 and 5xx replies. Because upsert matches on email and phone, retrying is safe: it won’t make a second contact.

Starting a workflow when the lead arrives

Once leads arrive with a tag, open Automation → Workflows and use the Contact Tag trigger (tag added: website-lead). From there you can text the customer, assign them to someone and create an opportunity in your pipeline. Replying quickly is most of the battle: see speed to lead.

Doing it in RoughFigure

In RoughFigure we’ve built the HighLevel connection for you. Open Integrations → Send leads to your CRM, choose HighLevel, paste your Private Integration token and Location ID, and click Connect. That one click is all it takes: from then on every lead from your estimator arrives in the sub-account as a contact, with their name, email, phone, postcode and “RoughFigure” as the source, updated rather than duplicated if they’re already there.

Click Send a test lead to see “RoughFigure connection check” arrive, and if HighLevel is ever down we keep retrying for about four hours. See the HighLevel integration or read how to connect your CRM.

Questions

Do I need a HighLevel marketplace app to use the API?

No. For one sub-account sending leads into itself, a Private Integration token is enough. A marketplace app with OAuth is for software that many agencies install.

Should I use an agency token or a sub-account token?

A sub-account token. It can only reach that one location, which is what you want for a website form.

How often should I rotate the token?

HighLevel suggests rotating Private Integration tokens regularly, every 90 days or so. Make the new token, update your website or RoughFigure, then delete the old one.

Can I create an opportunity at the same time?

Yes, with a second call to the opportunities endpoint using the contact ID you got back. Many businesses find a workflow triggered by a tag simpler to change later.

Keep reading

Give your next customer a rough figure tonight.

14 days free · No card needed · Set up for you, free