Back to App
Back to Clients

The Client Timeline and Events from Your CRM

The Timeline tells you what happened to a client, in order: when the contract started, when it was paused, when a payment came in and how late it was, when a notification went out. You never have to write anything into it — it fills itself.

And when your CRM knows things SalesControl cannot — an onboarding call booked, accesses handed over, a campaign approved — you can let it tell the story too, through a webhook: every event you send becomes a Timeline entry, with its own date and time.

Plan required

The Timeline and event webhooks need a paid plan (Starter and above). See Subscription Plans.

Where to find it

Open a client from the Clients list and pick the Timeline tab. Each row shows the event and the moment it was recorded, in your own time zone.

It is not the same as the Logs tab. That one is the full technical record, restricted to people who hold the log permission, and it includes amounts and internal details. The Timeline tells the facts of the relationship without the confidential details — which is why the whole team can see it.

What gets recorded on its own

With nothing to configure, the Timeline already receives what SalesControl knows:

That history is a copy, not a link: if the lead is later deleted, the events carried over to the client stay where they are. And converting the same lead a second time creates no duplicates.

Note

The Timeline starts on 19 August 2026: earlier events are not reconstructed after the fact. A contract signed in June will have no “contract started” row, but everything from here on is recorded. The lead history has a birth date of its own: its record exists since 28 August 2026, so a lead worked before then only brings along what was recorded from that day onwards.

Events from the ecosystem (GodAds)

If your agency also uses GodAds and the two platforms are linked through the Ecosystem, the Timeline can also receive events coming from there. They only touch clients SalesControl matches with certainty (the rest wait in the Unmatched clients list of the Ecosystem card, where you match them by hand) and depend on two switches the Admin finds in that same card, both off until switched on.

How the ecosystem gets linked, how the automations are enabled and how waiting events get matched is covered in Linking SalesControl to the Ecosystem (GodAds).

Bringing in your CRM events

In the Admin Panel, Integrations tab, the Custom events card creates the addresses your CRM can post to.

With the GoHighLevel app a tag is enough

If you have connected the GoHighLevel app you do not need a webhook for each event: in the GoHighLevel tag events card you decide which tag, added to a contact in the agency’s or a client’s sub-account, writes which event in the Timeline. Tag events that find no client go to the same queue below, labelled GHL tag.

Create a webhook

New webhook, and give it a name you will recognise (“Onboarding”, “Accesses delivered”). Create as many as you like: one per kind of event keeps your automations tidy.

Copy the address

It ends with /functions/v1/client-event-intake?t=…. The part after t= is that webhook’s token: whoever holds it can write events into your agency, so treat it as a credential.

Paste it into your tool

In GoHighLevel: add the Webhook action to a workflow, method POST, then add the custom fields. In Zapier or Make: a POST request to the same address with a JSON body.

Use the ready-made templates

How to set it up opens two ready JSON bodies, one for each of the cases below, each with its own copy button.

Two cases, two templates: who is sending?

This is the part that matters most, and the only place where you can get it wrong without noticing.

When the workflow runs from the client sub-account

The normal case: the client has their own GoHighLevel sub-account and the automation runs there. The location variables point at them, so those are enough:

{
  "event": "Contratto firmato",
  "location_id": "{{location.id}}",
  "location_name": "{{location.name}}",
  "external_event_id": ""
}

On the first event SalesControl matches the client by name and learns the sub-account identifier: from then on every following event finds them immediately, even if the name in your CRM changes.

When the workflow runs from the agency sub-account

Some procedures about a client run from your sub-account — an onboarding call you book yourself, say. There the location variable is the agency, not the client. Declare it with location_type and name the client:

{
  "event": "Onboarding prenotata",
  "location_type": "agenzia",
  "cliente_nome": "Nome del cliente",
  "contact_id": "{{contact.id}}",
  "external_event_id": ""
}

The contact_id is what makes every later send automatic. In your own sub-account the contacts are your clients: on the first event SalesControl matches the client by name and learns that contact; from then on the name is no longer needed, and the event lands correctly even if the client gets renamed in GoHighLevel. Leave it out and everything still works — the name will simply be compared every time.

Why this really matters

Without that declaration, SalesControl would take the agency location for the client’s and link it to the first client matched by name. From that moment every event sent from your sub-account — including those about other clients — would land on them, with no visible error. With location_type set to agenzia the location is kept but ignored: it neither looks up the client nor ever gets linked.

The value to write is agenzia (agency works too). From client sub-accounts you simply leave the field out: its absence already means “I am the client”.

Accepted fields

FieldRecognised keysWhat it does
Event nameevent, evento, event_name, event_type, actionThe text that will appear in the Timeline. It may be omitted if the webhook has a default event name.
Sub-account idlocation_id, ghl_location_id, sub_account_id, account_idThe client sub-account. Once linked on the first match, this alone is enough.
Sub-account namelocation_name, sub_account, account_nameUsed for the first match, to recognise the client by name.
Sub-account typelocation_type, tipo_locationWrite agenzia when sending from your own sub-account. Leave it out otherwise.
Clientcliente_nome, client_name, customer_name, or cliente_idNames the client. This is the way to go when sending from the agency account.
Contactcontact_id, ghl_contact_id, id_contattoAgency sends only: the contact standing for the client. Linked on the first match, and from then on this alone is enough.
Event idexternal_event_id, event_id, idempotency_keyDeduplication key: if the same identifier comes back, the event is not recorded twice.

Key names are read case-insensitively. In GoHighLevel the workflow custom values land inside customData: the webhook reads those, the top level and contact alike — and a custom field left empty never wipes out the standard value of the same name.

When it happened

The date and time are those of arrival, taken from the server clock: that is what makes the Timeline evidence rather than a claim. If you want to record that something actually took place, send a second event at that moment — “Onboarding booked” when it is booked, “Onboarding held” when it happens.

How an event finds its client

Why two different keys

The two never swap places, and that is deliberate. In a client sub-account the location is the client, while the contacts are their leads: learning one would mistake a lead for the client. In your own sub-account it is the other way round — the location is you, the contacts are your clients.

The events waiting to be matched

Events that find no client are not lost: they stay in the Custom events card, under Events to match, showing the name and identifier that arrived. Pick the client and confirm: the event enters the Timeline with its own original date, not today’s.

If the event carried a client sub-account, matching it also links that sub-account, and the other waiting rows from the same source are settled along with it. The same holds for events that arrived from the agency carrying a contact_id: matching one links the contact, and the other rows from that contact are settled along with it. Without a contact, those events are matched one at a time: the agency location belongs to no client, so it cannot stand in for the others.

The link to your CRM

When a client is linked to a sub-account, their Timeline shows a Linked to your CRM label with an Unlink button. If they are linked to a contact as well — that is, if their events reach you from your own sub-account — the Linked to contact label sits beside it, with its own Unlink.

Unlinking stops events from arriving automatically by that route — they will go back to the matching queue — while the events already recorded stay where they are. The two links are independent: you can undo one and keep the other. Use it when a link was learned onto the wrong client, or when a client moves to a different sub-account.

Showing events in the client chat

Events can also appear in the client chat, as annotations in the middle of the conversation rather than messages from someone. The client only ever sees the event name, never the details.

Only some events can reach the chat: signing steps, appointments, the lead history and internal corrections always stay in the Timeline only.

Taking the Timeline out

If something does not work