Counting Trial Goals (events webhook)
A trial is almost always judged on a number: ten appointments booked, three deals closed, fifty leads delivered. The trial events webhook is an address your CRM calls every time that number goes up by one: SalesControl keeps the count for you and, once the target is reached, marks the trial as passed without anyone having to remember.
There is one address for the whole agency: you set it up once and reuse it in every client’s workflow, GoHighLevel snapshots included. Working out which client an event belongs to is SalesControl’s job, based on what the message carries.
This feature requires a paid plan (Starter and above). See Subscription Plans.
Where to set it up
Open the Admin Panel and go to the Integrations tab: the Trial events (webhook) card is reserved for admins. The target and the window, instead, live where contracts live: on the Services page and in the client form.
Creating the address
Until you do, the card says no address has been generated. One click creates the agency token and shows the full address, which ends with /functions/v1/trial-events?t=a1b2c3…: the part after t= is the token.
The Copy address button puts the full address, token included, on your clipboard. It is the same for every client: you don’t need one per client.
In GoHighLevel: Automations › Workflow › add the Webhook action with the POST method at the point where the event happens (after an appointment is booked, for example). In Zapier or Make: Webhooks module, POST method, then build the JSON body with the fields below.
In the card, How to connect it (GHL / Zapier / Make) opens the instructions along with two ready-to-copy JSON bodies, one for GoHighLevel and one for Zapier or Make.
The accepted fields
The minimum requirement is that either the external key or the client name arrives: with neither, the message is rejected, because there would be no way to tell whose event it is. Everything else is optional.
| Field | Recognised keys | What it’s for |
|---|---|---|
| External key | external_id, location_id, source_id | A stable identifier for the client in your CRM. In GoHighLevel it is {{location.id}}. |
| Client name | client_name, location_name, business_name, nome_business, name, nome | The name the client is registered under in SalesControl. It is what the first match uses. |
| Event label | event, evento, event_label, event_type, tipo_evento | Free text shown in the log. The templates use goal: leave it, or write the unit you are counting. It never changes the count. |
| Event id | event_id, idempotency_key | The identifier of the appointment or the deal: it stops the same event from being counted twice. |
| Event date | occurred_at, event_date, data_evento, date, data, timestamp | When it happened. If it is missing or unreadable, the time of arrival is used. |
Key names are read case-insensitively, so LOCATION_ID is worth the same as location_id. Underscores, however, are not ignored: the camelCase forms that are recognised are only the ones with an explicit alias — locationId, sourceId, clientName, locationName, businessName, eventId, idempotencyKey. For every other field use the underscore form: occurred_at, event_date, event_type. Dates are accepted both as DD/MM/YYYY and as YYYY-MM-DD; if the format can’t be read the event is still recorded, using the time it arrived.
The GoHighLevel shape
GHL doesn’t send every field at the same level: workflow custom values end up inside customData. The webhook reads customData, the top level and contact alike. When the same key appears twice, customData wins, then the top level, then contact — but a custom field left empty never wipes out the standard value with the same name.
Never count the same event twice
When an automation doesn’t get an answer quickly, it tends to retry: without a safeguard the same appointment would be counted several times and the trial would pass too early. That is why it pays to always send event_id with the identifier of the appointment or the deal: if the same identifier comes back, SalesControl answers ok but does not increase the count.
The same safeguard covers the most common slip when you are wiring up a lot of automations: the same address pasted into two or three different workflows that fire on the same fact. The event arrives several times there too, but with event_id filled in it is counted only once. Without event_id, SalesControl has no way of knowing it is the same fact and counts every call.
Counting an event from a GoHighLevel tag
With the GoHighLevel app connected you need no workflow at all: the tag that counts for the trial is set as a rule in the GoHighLevel tag events card (Admin › Integrations › Webhooks), with scope Clients’ sub-accounts and action Counts for the trial. The Trial events (webhook) card points you there with the Go to the rules link; a tag you had already set there has become a rule on its own. Every time a contact in the sub-account of a client with a running trial gets that tag, the trial counts one event: to count the same contact again, the workflow has to remove and re-add the tag. It applies only to clients with a connected sub-account. In the client page’s event log these events carry the GHL tag badge. Target, window and trial closing stay as always, and the webhook address keeps working for anyone not using GoHighLevel.
How an event finds its client
On arrival, every event is matched to a client in two steps:
- if the external key is already linked to a client, the match is immediate;
- otherwise the name is compared, ignoring case, accents and double spaces. Exactly one matching client: matched. Several clients with the same name, or none at all: the event waits and goes to reconciliation.
The first successful match learns the link: the external key is saved on the client and from then on later events find it straight away, even if the name in your CRM changes. A client that already has a key is never silently re-linked to a different one.
Events left without a client
Whenever events are waiting, a yellow band appears at the top of the Clients page — admins only — with the count and a Sort them out button. It opens the Trial events to attach window, where each event shows the name and key that arrived, the date and, for ambiguous cases, the candidate clients.
- Assign attaches the event to the client you pick. If the event carried an external key, that key is linked to this client (and detached from any other): later events will attach on their own.
- Ignore archives the event: it stays on record but counts for nobody.
- The Apply to every event from the same source checkbox, on by default, applies to Assign only: it attaches to the same client, in one go, every other waiting event that arrived with the same key or the same name. It is why nothing is lost even when the webhook fires before the client exists in SalesControl. Ignore, on the other hand, always acts on one event at a time: if you have many to discard, you archive them one by one.
Target and window: where you set them
Two values drive the automatic outcome, and they live on two levels:
| Where | Fields | Effect |
|---|---|---|
| Services (the template) | Trial event target, Trial window (days) | The defaults for every client starting on that service. |
| The client form (when the Trial checkbox is on) | The same two fields | They apply to that client only. Left empty, the service values are used. |
On creation the two fields are pre-filled with the values of the service you pick; when editing they show what is currently set on the client.
With no target, events are still counted and shown: they simply never close the trial on their own. It is the easiest way to start measuring before you decide what the right threshold is.
The window does not start at signature, but on the day you press 🚀 Activate campaigns in the Clients list: until you do, there is no deadline at all. See Managing the Trial Period.
What you see while the trial runs
- In the Clients list, the Trial column shows a progress badge such as 7/10 · 12d: events collected out of the target, and days left in the window. If the window hasn’t started only the two numbers are shown and the tooltip reminds you to activate the campaigns; once the window is running, the tooltip tells you how long it lasts.
- On the client page, the Trial events card shows the count, a progress bar and the end of the window, with the Event log that opens on a click and lists every event with its date, label and origin (Webhook or Manual).
The label on every row of the log is exactly the text you put in the event field: the templates use goal, and leaving it that way is perfectly fine. If you prefer, write the unit you are counting — appointment, contract, lead — so whoever reads the log sees at a glance what it is about. It makes no difference to the count: every event is worth +1, whatever label it carries.
Admins can correct the count: Add an event by hand records an event your CRM never sent, while the bin icon on a row excludes it — the event stays in the log as a trace, but stops counting.
An event attached to a client is always counted, even if it arrives before campaign activation or once the trial is already closed. The target and the window decide the outcome, never the count.
When the target is reached
If the client’s trial is running and the count hits the target, the trial is automatically closed as passed, with the same effects as the manual button: the contract becomes active (installment due dates stay as calculated at signing), any retroactive bonuses tied to the signing month kick in, and the closure is recorded in the client timeline as a system action.
When the target is reached thanks to a manual correction instead, the card shows a Mark trial as passed button: closing it stays your call, with the same effects.
When the window expires without the target
The check runs once a day and you decide what happens, with the Automatic failure at the end of the window checkbox in the card:
- Off (the starting setting): you get a single “window expired” notification and decide whether to close the trial or give it more time. The warning is not repeated on the following days.
- On: the trial is closed as failed, exactly as if you had pressed the button yourself — contract value zeroed, pending installments removed, entry recorded in the history.
If automatic failure is off and the Trial Window Expired notification is disabled on both channels, nothing happens when the window ends: the trial stays open and you only notice from the badge in the list. If you rely on the warning, switch it on.
The notifications
Three notifications follow the trial lifecycle, and they are off by default on both channels. You switch them on in Admin Panel › Notifications:
| Notification | When it fires |
|---|---|
| Trial Passed | The trial is passed, either by reaching the target or by closing it manually. |
| Trial Failed | The trial is closed as failed, by automatic failure or manually. |
| Trial Window Expired | The window ends without the target being reached, with automatic failure off. |
Recipients are chosen channel by channel among setter, closer, assigned PM, admin and sales manager. The client is available on email only, because clients have no push notifications: if there is no email address on their record, they are simply skipped.
Managing the address over time
- The card shows how many events have been recorded across the whole agency — including the ones added by hand and the ignored ones — and when the address was last used: it is the quickest way to tell whether the integration is alive.
- Disable suspends the address without deleting it: calls stop working until you enable it again. Events already received are kept.
- Regenerate token creates a new address and invalidates the previous one: you will have to paste the new one into every connected workflow, or they stop delivering.
The address contains a token: whoever holds it can push your agency’s trial counts up, and therefore make trials pass. They cannot read or change anything else. Treat it as a credential, share it only with whoever configures the automations and, if it ends up in the wrong place, use Regenerate token.
If something doesn’t work
- Your tool gets a 404: the address is wrong or incomplete, the token no longer exists (regenerated) or the address is disabled.
- 403 error: the agency is on the Free plan, which does not include the trial events webhook.
- 422 error: the message carried neither the external key nor the client name. Check that the workflow custom values are actually filled in.
- The count doesn’t move but the received-events number in the card goes up: events are arriving but not finding the client. Look for the yellow band on the Clients page and assign them.
- The count goes up too fast: your tool is most likely retrying the same event. Add
event_idto the message body. - The target is reached but the trial doesn’t close: that happens when the trial is already closed, or when the target was reached with events added by hand or assigned from the reconciliation queue — only the events the webhook attaches on its own close the trial automatically, in every other case use Mark trial as passed in the card.
- The progress badge doesn’t appear: that client has no target set, neither on their own form nor on the service, or their trial is already closed — passed or failed alike. The badge only accompanies running trials.