Receiving Leads from GHL, Zapier or Make
A lead-in webhook is an address your CRM (GoHighLevel, Zapier, Make or any tool that can send an HTTP call) uses to hand contacts over to SalesControl. As soon as the webhook receives a contact, it shows up on the Leads page, already assigned.
This feature requires a paid plan (Starter and above). See Subscription Plans.
Where to set it up
Open Settings › Integrations and unfold the Webhooks group: the Lead-in Webhooks card is reserved for admins.
Creating a webhook
Give it a recognisable Name (for example “GHL — calls”): it’s how you tell where leads came from once you have more than one.
Default source (for example “Facebook Ads”) is used when the contact arrives without one. Default setter and closer are the safety net for contacts that carry no name, or a name that can’t be matched.
The Copy URL button puts the full webhook address, token included, on your clipboard.
In GoHighLevel: Automations › Workflow › add the Webhook action with the POST method and paste the URL. In Zapier or Make: add the Webhooks module, choose POST, paste the URL and build the JSON body with the fields below.
In the card, under the list, How to connect (GHL / Zapier / Make) opens the same instructions together with a ready-to-copy JSON template.
The accepted fields
The only requirement is that either a name or an email arrives. Everything else is optional.
| Lead field | Recognised keys |
|---|---|
| Full name | name, nome, full_name, contact_name. When missing, it is composed from first_name + last_name. |
| First name and Last name | first_name (or firstname) and last_name (or lastname): they are now stored separately too, not just merged into the full name. An empty value never clears a part already on record. |
email, email_address | |
| Phone | phone, telefono, mobile, tel, phone_number |
| Company | company, azienda, company_name, business_name, organization |
| Source | source, source, lead_source, contact_source |
| Deal status | note, notes, note, message, description, stato_trattativa |
| Discovery call | discovery_call_date, call_date, call_conoscitiva, appointment_date — or, automatically, calendar.startTime from GHL appointment triggers |
| Last follow-up | last_followup, last_contact_date, ultimo_followup |
| Next follow-up | next_contact_date, follow_up_date, prossimo_contatto |
| Notes (what to say) | what_to_say, what_to_say, next_contact_note |
| Setter | setter, setter_name, venditore |
| Closer | closer, closer_name |
| Language | language, language, lead_language, locale |
In the lead form, language is a closed dropdown over the five app languages, but the webhook still accepts any text and stores it as is. A payload with “Inglese” or “English” is not recognised: the lead is created all the same, but that language plays no part in deriving the market and shows as “Not set” when you reopen the lead for editing. Send the codes it, en, es, de, fr.
Dates are accepted both as DD/MM/YYYY and as YYYY-MM-DD (a trailing time is ignored). If the format can’t be read, that single field is left empty but the lead is still created: you never lose a contact over a date.
The GoHighLevel shape
GHL doesn’t always send fields at the same level: contact data may sit inside contact and custom values inside customData. The webhook reads both. When the same key appears twice, customData wins, then the top level, then contact — but an empty custom field never wipes out the standard value with the same name.
If your GHL workflow starts from an appointment trigger (a calendar booking), GHL includes a calendar object with the appointment time in the message on its own: the webhook reads calendar.startTime (start_time and starttime are accepted too) and fills in the discovery call automatically — no need to configure a custom field carrying the date. The explicit fields in the table (discovery_call_date and the others, in customData, at the top level or inside contact) win whenever they contain a readable date; only when they are missing, or in a rejected format, does the webhook fall back to calendar.startTime. The call time is read from the same string the date was understood from — unless the message carries a dedicated time field (e.g. call_ora, appointment_time), which always wins. A time is never taken from a rejected date.
Setter and closer by name
In the setter and closer fields you can send the team member’s name: it is matched automatically even when written short (first name, surname or an initial). If the name is ambiguous (two members would fit) or can’t be matched at all, the webhook’s default setter and closer are used instead. The lead is created either way.
Contacts you already have
If a contact matches a lead that is already Open — same email, or same phone, even when the email in the message differs from the one on record — no duplicate is created: the fields present in the message update the existing lead, the missing ones are left alone. Setter and closer only change if the message carries a name that can be matched. In this case the “New Lead” notification does not fire.
The order is this: if an email is there and matches an Open lead, it always wins. If the email is new (no Open lead uses it) or missing altogether, the phone is compared instead: the number is normalised by looking at its last 9 digits, so international prefixes don’t matter, and when the numbers match the existing lead is updated. It’s the typical case of the same person filling in the form with one email and then booking the call with another, but with the same phone number.
A Lost lead blocks nothing: an archived contact that comes back creates a brand-new lead.
Someone who is already a client (or a Won lead) no longer becomes a lead: a person is either a lead or a client, never both. The contact is recognised by GHL contact id, email or phone. If the message carries an appointment, the call shows up in the Agenda on the client’s page; if it is a form without an appointment, the request is saved as a From webhook note in the client’s Notes section, with the webhook name, the source and the text received. This is why every client should have email and phone filled in on their page: without them the webhook cannot recognise them.
The “New Lead” notification
It is off by default. To switch it on go to Admin Panel › Notifications, find New Lead and choose channels and recipients (setter, closer, admin, sales manager).
Receiving notes from GHL (“Note only” webhook)
Every webhook has two addresses. The first, Leads and appointments, is the one described so far. The second, Note only, brings a note written in GoHighLevel into SalesControl: it uses the same token, so Disable and Regenerate token apply to both.
In GoHighLevel create a workflow with the Note Added trigger and a Webhook action (POST method) to the Note only URL. GHL sends the contact and the note text on its own: the card’s Template for the “Note Added” trigger is only needed with Zapier, Make or a custom webhook. The note appears in the Notes section of the lead’s page (or the client’s, if the lead has already been converted) with the From webhook label.
The lead is neither created nor changed: if the contact does not exist in SalesControl the note is ignored, with no error for GHL.
| Data | Recognised keys |
|---|---|
| Note text | note, nota, note_body (also notes, note_text, body, message, text). GHL’s standard shape, note.body, is read on its own; a note sent as HTML is turned into plain text. Up to 5000 characters. |
| Contact | contact_id (the GHL contact id), email, phone: one of them is enough. |
The contact is matched in this order: first the GHL contact id, then the email, finally the phone. For each one an Open lead is looked for first, then an already converted lead, finally a client. If the value matches more than one contact, the note is not saved.
If the same note arrives twice for the same contact within 10 minutes (for example when GHL retries the call), it is saved only once.
Managing webhooks over time
- Each webhook shows how many leads it received and when it was last used: the quickest way to tell whether the integration is alive.
- Disable suspends the webhook without deleting it: calls stop working until you enable it again.
- Regenerate token creates a new URL and invalidates the old one: you’ll have to paste the new address into the external tool.
- Delete removes the webhook. Leads already received are kept.
The URL contains a token: whoever holds it can push or update leads and add notes to your agency’s contacts. They cannot read anything, though. Treat it like a credential and, if it ends up in the wrong place, use Regenerate token.
If something doesn’t work
- Your tool gets a 404: the URL is wrong, the token no longer exists (it was regenerated) or the webhook is disabled.
- A 403: the agency is on the Free plan, which doesn’t include lead-in webhooks.
- The lead arrives with no setter and no closer: the message carried no recognisable name and the webhook has no defaults set.
- The lead doesn’t show up: check that you are an admin, or that you are the assigned setter or closer — otherwise it isn’t in your list.