GoHighLevel Webhooks: Inbound & Outbound Setup (2026) — HL Growth Partner, Dr Priya Jaganathan

GoHighLevel Webhooks: Inbound & Outbound Setup (2026)

August 24, 2026

GoHighLevel Webhooks: Inbound & Outbound Setup (2026)

By Dr Priya Jaganathan, GoHighLevel Certified Admin · HL Growth Partner, Australia · Updated 24 August 2026 · 8 min read

GoHighLevel webhooks are the cheapest, fastest way to move data between your sub-account and anything else you run — a booking engine, an accounting platform, a custom database, an AI agent. They are also the single most misconfigured feature I find when I audit an account. Nine times out of ten the workflow looks fine on the canvas, but the payload never arrives, or it arrives and every custom field is blank.

This guide walks through both directions: the Inbound Webhook trigger that lets an outside system start a Workflow, and the Custom Webhook action that pushes contact data out to a URL you control. I will cover payload mapping, headers and auth tokens, what Premium Actions actually cost in execution credits, how to read the execution logs when a call fails, and when you should skip the webhook entirely and use Make, Zapier or n8n instead. Everything here is written from builds I have shipped for Australian agencies and their clients.

What a webhook actually is, in GHL terms

A webhook is just an HTTP request carrying a small JSON body. There is no magic in it. The only thing that changes is who is making the call and who is listening.

Inside HighLevel that maps to two distinct elements. The Inbound Webhook trigger gives you a unique URL. Anything that can send an HTTP POST to that URL — a form on a WordPress site, a Shopify app, a Python script, an n8n node — will start that Workflow and hand it a JSON payload. The Custom Webhook action is the reverse: a step inside a Workflow that fires a POST, GET, PUT or DELETE out to a URL you specify, optionally carrying contact data, custom field values and pipeline information in the body.

Everything else — tags, custom fields, opportunity stages, Premium Actions — sits around those two elements. Get the direction clear in your head first, because the debugging steps for each are completely different.

Setting up the Inbound Webhook trigger

Create a new Workflow in the sub-account, add a trigger, and choose Inbound Webhook. GoHighLevel immediately generates a unique endpoint that looks roughly like https://services.leadconnectorhq.com/hooks/<locationId>/webhook-trigger/<uniqueId>. Copy it. That URL is the whole trigger — there is no secret to configure, which has security implications I will come back to.

Send a sample payload before you build anything

This is the step people skip, and it is the reason their mapping dropdowns are empty. GoHighLevel cannot guess the shape of your JSON. It needs to see a real example first so it can build the data picker.

Either fire a genuine request from the source system, or paste a sample into the trigger's test area. A clean payload for a booking notification might look like this:

{
  "first_name": "Alex",
  "last_name": "Nguyen",
  "email": "[email protected]",
  "phone": "+61412345678",
  "booking": {
    "reference": "BK-88201",
    "service": "Initial Consult",
    "value": 249.00
  }
}

Keep your keys flat where you can. Deeply nested arrays are supported, but referencing the third object inside an array inside an object gets brittle fast, and one change at the source silently breaks your mapping.

Mapping payload values to custom fields

Once a sample has been received, add an Update Contact or Create/Update Contact action. In any field, open the data picker and you will now see an Inbound Webhook branch listing every key from your sample. Select booking.reference and it drops in as a merge token such as {{inboundWebhookRequest.booking.reference}}.

Two rules I enforce on every build. First, create the custom fields in Settings before you build the Workflow — naming them on the fly leads to duplicates like "Booking Ref" and "Booking Reference" sitting in the same sub-account. Second, always map email or phone into the contact record, because without one of them GoHighLevel has nothing to match on and you will manufacture duplicate contacts at speed.

From there the Workflow behaves like any other. Add tags, move an opportunity, branch the logic. If you are routing different payload types down different paths, the cleanest approach is covered in my guide to GoHighLevel workflow If/Else conditions — branch on a payload value like event_type rather than building four near-identical workflows.

The Custom Webhook action: sending data out

The Custom Webhook action is how a Workflow talks to the outside world. Add the action, choose your method (POST for most integrations, GET for simple lookups), and paste the destination URL.

Headers, auth tokens and query parameters

You get three configuration areas. Headers is where authentication belongs — typically Authorization: Bearer your_token_here or a vendor-specific key such as x-api-key. Query parameters are appended to the URL and should be reserved for non-sensitive routing values like source=ghl or a record ID. Body carries the JSON payload.

Build the body with the merge picker rather than typing tokens from memory:

{
  "contact_id": "{{contact.id}}",
  "name": "{{contact.first_name}} {{contact.last_name}}",
  "email": "{{contact.email}}",
  "phone": "{{contact.phone}}",
  "lead_score": "{{contact.lead_score}}",
  "source": "highlevel-workflow"
}

Set Content-Type: application/json explicitly. A surprising share of 400 errors are receiving systems that will not parse a body sent without it. If you are pushing a calculated value out, make sure the field is populated before the webhook fires — that ordering problem is exactly why GoHighLevel lead scoring workflows need their scoring maths completed a step earlier, not in parallel.

Inbound vs outbound GoHighLevel webhooks compared

Aspect Inbound Webhook (trigger) Custom Webhook (action)
Direction External system → GoHighLevel GoHighLevel → external system
GHL element used Inbound Webhook trigger on a Workflow Custom Webhook action inside a Workflow
Typical use case External booking, payment or form event creates or updates a contact Push a new lead to a CRM, ERP, Slack or an AI endpoint
Auth method None built in — obscure URL plus a shared secret you validate yourself Headers you control: Bearer token, API key or basic auth
Cost Free — standard workflow execution Premium Action — consumes execution credits per call
Common failure No sample payload sent, so mapping fields come back empty 401 from a wrong or expired token; timeout from a slow endpoint

Premium Actions and execution credits

The Custom Webhook action is a Premium Action. So are Custom Code, Google Sheets, Slack and the AI actions. Every time one executes it draws from your sub-account's execution credit balance, billed on the wallet at agency level.

Individually the cost is trivial. At volume it is not. A workflow that fires an outbound webhook on every inbound SMS in a busy sub-account can burn through a month's credits in a fortnight, and because the charge is per execution rather than per contact, a loop or a badly scoped trigger multiplies it quickly. I have seen a re-entry-enabled workflow fire the same webhook eleven times for one contact.

Two habits keep this in check. Batch where you can — one webhook carrying five fields beats five webhooks carrying one. And gate the webhook behind an If/Else so it only fires for the contacts that actually need it. The full breakdown of what draws credits and roughly what it costs sits in my article on GoHighLevel Premium Workflow Actions costs.

Testing and debugging HighLevel webhooks

Test with a throwaway endpoint before you point anything at production. A free request-bin service or a simple n8n webhook node will show you the exact headers and body GoHighLevel sends, which removes all guesswork about whether the problem is on your side or theirs.

Then use the Workflow's execution logs. Open the Workflow, go to the Execution Logs tab, click the contact, and expand the webhook step. You get the request that was sent, the response status code and the response body. That is usually enough to diagnose the fault in under a minute.

What the common status codes mean

  • 400 Bad Request — malformed JSON, almost always a merge token that resolved to nothing and left a dangling comma, or a missing Content-Type header.
  • 401 / 403 — wrong, expired or rotated token, or a token pasted with a trailing space. Re-paste it rather than eyeballing it.
  • 404 — the endpoint path changed, or you are pointing at a sandbox URL from a production sub-account.
  • 422 — the receiver understood the request but rejected a value, usually a date format or a phone number that is not in E.164.
  • Timeout — the receiving system took too long. Have it acknowledge with a 200 immediately and do the heavy work asynchronously.

If the webhook step never appears in the logs at all, the problem is upstream: the trigger filter excluded the contact, or an earlier action failed. My guide to GoHighLevel workflow troubleshooting walks through that diagnostic order. HighLevel's own API and integrations documentation is the reference for payload shapes and endpoint behaviour, and the official help centre covers Workflow-level settings.

Webhook vs Zapier, Make or n8n

A raw webhook is the right tool when the connection is one-to-one, the payload is simple, and the receiving system publishes a documented endpoint. It is free on the inbound side, near-instant, and has no third-party dependency.

Reach for a middleware platform when you need transformation, iteration or error handling that GoHighLevel does not provide natively. Splitting a comma-separated string, looping over line items in an order, retrying a failed call with backoff, or fanning one event out to four systems — all of that belongs in Make or n8n, not in a chain of Custom Webhook actions.

My rough rule: fewer than three steps and one destination, use a native webhook. Anything with conditional data reshaping goes to middleware. Cost comparisons for each option are in my write-ups on the GoHighLevel Zapier integration and the GoHighLevel n8n integration, which is usually the cheapest at volume if you are willing to self-host.

Security: the bits people skip

Never put an API key in a query string. Query strings get logged by proxies, load balancers and server access logs, and once a key is in someone's log file you have to assume it is compromised. Authentication belongs in headers.

Your Inbound Webhook URL has no authentication of its own — anyone who knows it can trigger your Workflow. Treat it as a secret. Do not paste it into public support forums or client-facing documentation. Better still, have the sending system include a shared secret value in the payload, then make the first step of the Workflow an If/Else that checks it and drops anything that fails.

Rotate outbound tokens on a schedule and whenever a contractor leaves. Keep each sub-account's credentials separate so one leak does not expose a portfolio. And validate what arrives — an inbound payload is untrusted input, so check that an email looks like an email before you write it to a contact record.

Common mistakes to avoid

  • Building the mapping before sending a sample payload, then wondering why the data picker is empty.
  • Putting an API key or token in the query string instead of an Authorization header.
  • Leaving workflow re-entry enabled on a webhook workflow, which duplicates outbound calls and burns execution credits.
  • Firing a Custom Webhook immediately after a Create Contact action without a short wait, so the record is not fully written when the payload is built.
  • Hard-coding a sub-account ID or location ID into a snapshot, so every cloned account posts data to the original client's endpoint.
  • Never checking execution logs after go-live — webhooks fail silently, and nobody notices until a month of leads has gone missing.

On that last point, a well-placed wait step also solves a lot of race conditions. My guide to GoHighLevel wait steps and goal events covers where to place them without stalling the whole workflow.

If you want your GoHighLevel webhooks and workflows built properly the first time — with logging, retries and no silent failures — book a strategy call with the HL Growth Partner team.

Book Your Strategy Call →

Frequently asked questions

Do GoHighLevel webhooks cost anything to use?

Inbound webhooks are free — receiving a payload and running the Workflow uses no special credits. The outbound Custom Webhook action is a Premium Action and consumes execution credits from your agency wallet on every call, so high-volume outbound integrations need budgeting.

Why is my Inbound Webhook data not mapping to custom fields?

Almost always because no sample payload has been received. GoHighLevel builds the data picker from an actual request, so send a real or test payload first, refresh the trigger, then open the field mapping dropdown. If keys still do not appear, check your JSON is valid and that you are sending it as a POST body rather than form data.

Can I secure an Inbound Webhook trigger URL?

There is no built-in signature verification. The practical approach is to treat the URL as a secret, include a shared token as a value in the payload, and add an If/Else condition as the first workflow step that ends the workflow when the token does not match.

How many Custom Webhook actions can I put in one Workflow?

There is no hard cap, but each one is a separate Premium Action charge and adds latency. If you need more than two or three outbound calls in a sequence, push the orchestration into Make or n8n and have GoHighLevel send a single webhook.

What is the difference between a webhook and the GoHighLevel API?

A webhook is event-driven — something happens and a payload is pushed automatically. The API is request-driven, where you ask for data when you need it. Webhooks suit real-time notifications; the API suits lookups, bulk operations and anything needing a response you act on immediately.

Dr PriyaJaganathan

Dr PriyaJaganathan

Dr Priya Jaganathan is a Go High Level Certified Admin, trusted CRM consultant based in Australia, and a keynote speaker at SaaSpreneur Sydney and Level Up 2025 in Dallas.

Back to Blog