IntegrationsCRMGoHighLevel

GoHighLevel

Datafly Signal upserts contacts into your GoHighLevel (HighLevel / LeadConnector) sub-account server-to-server using the v2 Contacts API. When a visitor identifies, signs up, or submits a lead, Signal creates or updates the matching contact in your CRM — no client-side form-tracking snippet required.

Prerequisites

Before configuring GoHighLevel in Signal you need a HighLevel sub-account (Location), a Private Integration token scoped to write contacts, and the Location ID of the sub-account you want contacts written to.

Step 1: Find your Location ID

  1. Open the HighLevel sub-account you want contacts written to.
  2. Go to Settings > Business Profile. The Location ID is shown there (it also appears in the browser URL when you are inside the sub-account, e.g. .../location/ve9EPM428h8vShlRW1KT/...).
  3. Copy the Location ID.

Step 2: Create a Private Integration token

Private Integrations are static access tokens you mint inside a sub-account.

  1. In the same sub-account, go to Settings > Private Integrations.
  2. Click Create New Integration.
  3. Give it a name (e.g. “Datafly Signal”).
  4. Under scopes, enable View Contacts and Edit Contacts (contacts.readonly and contacts.write).
  5. Click Create and copy the token immediately — it is shown only once.
⚠️

Create the token in the same sub-account whose Location ID you configured. A token from a different sub-account, or an agency-level token, will not write contacts to your Location. HighLevel recommends rotating Private Integration tokens every 90 days.

Configure in Signal

Configuration Fields

FieldRequiredDescription
access_tokenYesPrivate Integration token with the contacts.write scope. Sent as a Bearer token.
location_idYesThe sub-account (Location) ID that contacts are written to. Must match the token’s sub-account.

Management UI Setup

Add the integration

Go to Integrations > Add Integration > GoHighLevel and choose the Default variant.

Enter credentials

Paste your Private Integration Token and Location ID.

Select the consent category that governs marketing contactability (typically marketing). Signal uses it to set the contact’s do-not-disturb flag.

Save

Click Save. Signal begins upserting contacts on the next identify / sign-up / lead event.

API Endpoint

POST https://services.leadconnectorhq.com/contacts/upsert
Authorization: Bearer {access_token}
Version: 2021-07-28
Content-Type: application/json

The upsert endpoint matches an existing contact by email and/or phone according to the sub-account’s Allow Duplicate Contact priority, then creates or updates the contact. This makes re-sends idempotent — the same visitor does not produce duplicate contacts.

Identity Signals

GoHighLevel is your CRM of record, so contact fields are sent in clear (not hashed) — this is intentional and unlike ad-platform conversion APIs.

TraitContact fieldNormalisation
emailemailtrimmed, lowercased
phonephonenormalised to E.164 (e.g. +447700900123)
first_namefirstName
last_namelastName
namename
companycompanyName
addressaddress1
city / state / postal_code / countrycity / state / postalCode / country
websitewebsite

Provide identity by calling datafly.identify() when a user logs in, registers, or submits a form:

datafly.identify("user-123", {
  email: "jane.doe@example.com",
  phone: "+44 7700 900123",
  first_name: "Jane",
  last_name: "Doe",
  company: "Acme Ltd",
  city: "London",
  country: "GB"
});

Signal maps your canonical marketing consent decision to the contact’s dnd (do-not-disturb) flag:

  • marketing granted -> dnd: false (contact is reachable)
  • marketing denied -> dnd: true (contact is flagged do-not-disturb)

If your team manages do-not-disturb separately inside HighLevel, remove the dnd mapping from the integration’s Field Mappings so Signal leaves the flag untouched.

Event Mapping

The Default preset upserts a contact on identity-bearing events and drops everything else (analytics-only events such as product views never create empty contacts).

Signal eventAction
IdentifiedUpsert contact
Signed UpUpsert contact (sets source from sign-up method)
Lead GeneratedUpsert contact (sets source and a lead-source tag)
All other eventsDropped

Example: Lead Generated

Datafly.js call:

datafly.identify("lead-987", {
  email: "sam@example.com",
  phone: "+1 415 555 0142",
  first_name: "Sam",
  last_name: "Rivera"
});
 
datafly.track("Lead Generated", {
  source: "Pricing Page Form",
  lead_tag: "website-lead"
});

Payload sent by Signal to GoHighLevel:

{
  "locationId": "ve9EPM428h8vShlRW1KT",
  "email": "sam@example.com",
  "phone": "+14155550142",
  "firstName": "Sam",
  "lastName": "Rivera",
  "source": "Pricing Page Form",
  "tags": ["website-lead"],
  "dnd": false
}

Custom Fields

HighLevel custom fields are addressed by per-Location field IDs, so they are not included in the Default preset. To populate one:

  1. In HighLevel, go to Settings > Custom Fields and note the field’s ID.
  2. In the Signal integration’s Field Mappings, add a mapping with target customFields[].id set to that ID and customFields[].field_value set to your source property.

Testing

Trigger an identify

On your site, submit a form or call datafly.identify() followed by datafly.track("Lead Generated", ...).

Check the contact in HighLevel

Open Contacts in the sub-account. The contact should appear (or update) within a few seconds, with the email, phone, name, and any tag you sent.

Inspect in Signal

Open the Signal Event Debugger and confirm the event delivered with a 200/201 response from services.leadconnectorhq.com.

Troubleshooting

ProblemSolution
401 UnauthorizedThe Private Integration token is wrong or expired. Regenerate it in the sub-account and update the integration.
403 ForbiddenThe token is missing the contacts.write scope, or it belongs to a different sub-account than location_id. Recreate the token in the correct sub-account with Edit Contacts enabled.
422 UnprocessableThe contact has no usable identifier or an invalid email/phone. Ensure datafly.identify() carries at least an email or phone, and that phone normalises to E.164.
Duplicate contacts createdReview the sub-account’s Allow Duplicate Contact setting so upsert matches on the identifier you send (email and/or phone).
Contacts created but unreachable for marketingCheck the dnd mapping and your marketing consent category — denied marketing consent sets dnd: true.
Custom field not populatingCustom fields use per-Location field IDs. Add a customFields[].id mapping with the correct ID (see Custom Fields above).

Rate Limits

HighLevel enforces burst limits of roughly 100 requests per 10 seconds and a daily cap (around 200,000 requests) per sub-account. Signal applies a conservative sustained rate by default; for high lead volume, contact your Signal operator before raising it.