Guide
How to Use Webhooks in GoHighLevel Workflows
Webhooks are how GHL talks to everything it doesn't have a native integration for — in both directions. An inbound webhook lets an external form or app start a GHL workflow with its own data; an outbound webhook lets a GHL workflow hand contact or event data to any URL you point it at. Most of the confusion I see comes from people treating these as one feature when they're really two, configured in two different places. Here's how each one actually works.
On This Page
How to Set Up GHL Webhooks, Both Directions
Six steps covering inbound and outbound, in the order I'd actually work through them. If you're specifically wiring GHL up to n8n rather than a general external URL, the mechanics below still apply, but see my dedicated GoHighLevel + n8n guide for the n8n-side node setup.
Work out which direction you actually need
GHL webhooks run two separate directions and it's worth being clear on which one solves your problem before you touch the workflow builder. Inbound means an external system sends data into GHL to start or feed a workflow — a form on another platform, a third-party app posting a new record. Outbound means a GHL workflow sends contact or event data out to a URL you control — a custom API, a middleware step in front of a spreadsheet, a Slack notification. Plenty of real setups use both directions in the same workflow, but they're configured in completely different places, so pin down which one you're building first.
Set up the Inbound Webhook trigger
In Workflow Builder, add "Inbound Webhook" as the trigger and GHL generates a unique URL for that specific workflow — copy it into whatever external form, app, or platform needs to send data in. This is a premium trigger: sub-accounts get a batch of free executions before per-execution charges apply, so it's worth checking your account's usage if you're firing it at volume. The URL itself is the only setup at this stage; the actual field mapping happens once real data has hit it, in the next step.
Send a real test payload, then map the fields
Don't try to configure field mapping against a blank trigger — send one genuine test request to the webhook URL first (from the actual external system, or a tool like Postman), and GHL uses that real payload as the sample it builds the mapping screen from. From there you pick each incoming JSON key and map it to a contact field, a custom field, or a variable usable later in the workflow. One hard requirement: the payload needs an email or a phone number if you want it to create or update a contact — without one of those two, GHL has nothing to match the record on. Keep JSON keys as camelCase or snake_case without spaces, and don't rely on arrays inside custom values — you can send them, but you can't reference them inside workflow actions.
Add an outbound Webhook (or Custom Webhook) action where you need to push data out
For sending data out, GHL actually gives you two different action steps. The plain "Webhook" action is the simpler one: point it at a URL, and it POSTs an automatic payload built from the contact's standard fields, the location (sub-account) data, and whatever object triggered the workflow — an appointment's details are only included if the workflow was actually triggered by an appointment event, for example. It's enough for a lot of "just get this data over there" cases, including things GHL has no native integration for — a custom API, a Google Sheet sitting behind a middleware step, or an internal reporting database.
Reach for Custom Webhook when the receiving side needs auth or a specific shape
If the URL you're sending to needs an Authorization header, a specific JSON structure, or a method other than POST, the plain Webhook action won't cover it — that's what the separate "Custom Webhook" action is for. It supports GET, POST, PUT, and DELETE, lets you set custom headers and query parameters with merge fields like {{contact.email}} baked in, and offers Bearer token, API key, Basic Auth, OAuth2, or no-auth authentication, plus a raw body editor for building the exact JSON (or form-encoded) shape the other system expects. This is also the more realistic choice for a Slack notification with a formatted message, or any endpoint that rejects requests without a matching Authorization header.
Test both directions before you trust them with real leads
For outbound, point the action at a throwaway URL from a tool like webhook.site before switching it to the real endpoint — that shows you the exact payload GHL is sending, which the workflow's own Execution Logs won't display in full. For inbound, fire the same kind of real test request you used for field mapping and confirm the workflow actually ran and the contact fields landed where you expected. Do this before either direction touches production data — a webhook that looks configured correctly can still fail silently the first time it meets a real, messier payload.
When You'd Actually Use This
Webhooks earn their place when GHL's built-in triggers and actions run out of road. Four situations I see most often, across both directions.
Syncing GHL to a system it doesn't natively integrate with
GHL's marketplace covers the popular tools, but plenty of niche booking systems, internal databases, and industry-specific software aren't on it. If that system can receive or send a webhook at all, you can bridge it to GHL in either direction without waiting for an official integration to exist.
Triggering a custom notification
An outbound webhook to Slack (or any chat tool with an incoming webhook URL) turns a GHL event — a new lead, a stage change, a booked call — into an instant message to whoever needs to see it, formatted however you want, not just GHL's built-in internal notification templates.
Logging data externally for reporting
Piping contact and pipeline events out to a spreadsheet, a lightweight database, or a BI tool via webhook gives you reporting that lives outside GHL's own dashboards — useful when a client or team already reports out of another system and doesn't want to change that habit.
Starting a GHL workflow from an external form or app
The inbound direction flips the usual setup on its head: instead of GHL pushing data somewhere, a form on a landing page builder, a custom-built app, or another platform's own webhook feature can post straight into GHL and kick off a workflow, without that platform needing a dedicated GHL integration.
Frequently Asked Questions
Direction. The Inbound Webhook trigger sits at the start of a workflow and waits for an external system to send it data — that's how you get information into GHL. The Webhook (or Custom Webhook) action sits inside a workflow and sends contact or event data out to a URL you specify — that's how you get information out of GHL. They're configured in different places and don't depend on each other; you can use either one alone.
Sources: GoHighLevel's official documentation on the Inbound Webhook trigger, the Webhook (Outbound) action, and the Custom Webhook action.
Want the Whole Integration Built and Tested Instead?
Everything above is genuinely something you can wire up yourself inside GHL's workflow builder — this isn't a feature GHL locks behind developer access. If you'd rather not spend the afternoon on field mapping and auth headers, this is exactly the kind of build I do for clients: connecting GHL to whatever system it doesn't natively talk to, in either direction, through GoHighLevel automation or, where the logic gets more involved than a single webhook can handle, through n8n automation sitting alongside it.
- 7+ years building GHL workflows and integrations, including webhook-based connections to systems with no native integration
- 100,000+ leads delivered through automation systems built the same way this guide describes
- One real estate automation system alone responsible for 50,000+ of those leads
Have a GHL Webhook Question?
Fastest way to get an answer: message me directly on WhatsApp with what you're trying to connect. Or send the details below and I'll get back to you.