1import{j as e}from"./vendor-motion-CfBPC-kS.js";import{L as t}from"./vendor-react-BR_v4Ont.js";import{C as n}from"./ContentPage-BxMYAaUt.js";import"./index-VVKdn61F.js";import"./vendor-supabase-BSd6ADfF.js";import"./HireCTA-DktS03tb.js";import"./EmailGateModal-BCI8ZqlV.js";import"./dialog-my3VBzDi.js";const r=e.jsxs(e.Fragment,{children:["(",e.jsxs(e.Fragment,{children:[e.jsxs("p",{children:[e.jsx("strong",{children:"Last updated:"})," 2026-06-08 · ",e.jsx("strong",{children:"Tested on:"})," GoHighLevel sub-accounts, webhooks, n8n, and Supabase · ",e.jsx("strong",{children:"~7 min read"})]}),e.jsx("h2",{children:"Symptoms â what you're seeing"}),e.jsxs("ul",{children:[e.jsx("li",{children:"A reseller can sell the first few clients, but every new account still requires you to configure workflows, calendars, phone numbers, prompts, and permissions manually."}),e.jsx("li",{children:"Referral attribution disappears between the initial lead, the opportunity, the sub-account, and the eventual paying client."}),e.jsx("li",{children:"Partners either have too little access to operate their accounts or too much access to change shared infrastructure."}),e.jsx("li",{children:"Every client wants a slightly different AI voice setup, booking flow, pipeline, or qualification rule, so the âstandardâ onboarding keeps growing exceptions."}),e.jsx("li",{children:"Support volume increases with every partner instead of staying limited to the small number of platform-level failures."})]}),e.jsx("h2",{children:"Why this happens"}),e.jsx("p",{children:"GoHighLevel gives you useful agency and sub-account boundaries, but it does not automatically define your commercial model. Direct clients, web developers, resellers, and referral partners have different ownership and access requirements. Treating all four as the same account type creates the bottleneck."}),e.jsx("p",{children:"The recurring mistake is putting the entire operating model inside GHL snapshots and workflows. A snapshot can create a repeatable starting point. It cannot decide which partner owns a client, which credentials belong to that client, how a referral is attributed, or which configuration values are allowed to vary."}),e.jsx("p",{children:"I separate the system into four boundaries:"}),e.jsxs("ul",{children:[e.jsxs("li",{children:[e.jsx("strong",{children:"Commercial ownership:"})," who sourced the client, who sells the service, and who is responsible for the relationship."]}),e.jsxs("li",{children:[e.jsx("strong",{children:"GHL account ownership:"})," which agency and sub-account contain the clientâs contacts, opportunities, conversations, calendars, and workflows."]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Configuration ownership:"})," which values the partner can change and which values remain controlled by the platform operator."]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Automation ownership:"})," which workflows and external services can act on the account, and how failures are logged and retried."]})]}),e.jsxs("p",{children:["If those boundaries are not explicit, onboarding becomes a sequence of manual Slack messages and one-off workflow edits. That is also why a supposedly unrelated ",e.jsx(t,{to:"/fix/ghl-workflow-not-triggering",style:{color:"var(--tm-accent)"},children:"GHL workflow that is not triggering"})," becomes difficult to diagnose: nobody knows whether the problem is account configuration, permissions, a filter, or an external webhook."]}),e.jsx("h2",{children:"The architecture I use for partner-led GHL delivery"}),e.jsx("p",{children:"I keep the GHL sub-account as the operational boundary for each client. I keep partner attribution, onboarding state, and configuration metadata in a separate control plane. That control plane can be Supabase or another database with row-level access rules; the important part is that it is not hidden in free-form notes or a collection of tags."}),e.jsx("p",{children:"A minimal onboarding record looks like this:"}),e.jsx("pre",{style:{background:"var(--tm-code-bg)",color:"var(--tm-code-text)",padding:16,borderRadius:4,marginTop:8,fontSize:13,overflowX:"auto"},children:`{ 2 "client_id": "internal-client-id", 3 "ghl_location_id": "verified-sub-account-id", 4 "partner_id": "internal-partner-id", 5 "acquisition_type": "reseller", 6 "onboarding_status": "awaiting_credentials", 7 "configuration_version": 3, 8 "allowed_features": ["voice_agent", "calendar_booking"], 9 "referral_source": "partner-campaign-id" 10}`}),e.jsx("p",{children:"The identifiers above are deliberately internal placeholders, not values to paste into GHL. The record gives every automation a stable key and a known state. It also gives support one place to inspect before opening five different workflows."}),e.jsx("h2",{children:"The fix, step by step"}),e.jsxs("ol",{children:[e.jsxs("li",{children:[e.jsx("strong",{children:"Define the partner types before building more workflows."})," Write down the difference between a direct client, web developer, reseller, and referral partner. For each type, define who can create a client, who can access the GHL sub-account, who owns billing and support, and who can change configuration. If two types have identical permissions and ownership, they may not need separate automation paths."]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Create one client record outside the workflow builder."})," Store the partner ID, client ID, GHL location ID, onboarding status, configuration version, referral source, and enabled capabilities in a controlled data store. Do not use a contact tag as the system of record. Tags are useful for triggering or filtering actions; they are not reliable ownership records."]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Make onboarding state explicit."})," Use states such as ",e.jsx("code",{children:"invited"}),", ",e.jsx("code",{children:"account_created"}),", ",e.jsx("code",{children:"credentials_pending"}),", ",e.jsx("code",{children:"configuration_pending"}),", ",e.jsx("code",{children:"tested"}),", and ",e.jsx("code",{children:"active"}),". Each transition should have one responsible event or action. A client should not be marked active merely because a snapshot was applied."]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Use a versioned configuration contract."})," Separate shared defaults from client-specific values. Shared defaults can include the baseline pipeline and workflow structure. Client-specific values include the calendar ID, business hours, phone number, voice-agent prompt, transfer number, notification recipients, and consent copy. Reject incomplete configurations before provisioning downstream services."]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Provision from an idempotent event."}
10)," Send one onboarding event containing the internal client ID and configuration version. The consumer creates or updates the required GHL resources, records the result, and can safely receive the same event again. Do not create duplicate calendars, workflows, or webhook subscriptions when a retry occurs."]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Keep partner-facing actions narrow."})," A reseller should be able to initiate the actions they own, such as submitting client configuration or requesting a test call. They should not need unrestricted access to shared credentials, the central automation database, or unrelated client sub-accounts. Use the narrowest GHL user and sub-account permissions that support the agreed operation â exact permission labels vary by GoHighLevel plan, so confirm them against the account you're provisioning."]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Track referral attribution at the first durable event."})," Capture the referral or partner identifier when the lead first enters the system, then copy it to the opportunity and client record. Preserve the original value instead of replacing it whenever a later campaign or user edits the contact. If the referral is submitted through a webhook, validate the payload and store the normalized identifier before triggering downstream actions."]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Test the complete path with one synthetic client."})," Verify account creation, permissions, snapshot or template application, calendar booking, voice-agent handoff, CRM write-back, referral attribution, duplicate-event behavior, and failure notifications. A successful workflow execution is not enough if the final GHL record has the wrong owner or missing source."]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Measure exceptions, not just signups."})," Record the reason an onboarding item stopped: missing phone number, invalid calendar, unavailable permission, failed webhook, duplicate location, or incomplete prompt. The scalable system is the one where a partner can resolve a known exception without asking you to inspect the entire stack."]})]}),e.jsxs("p",{children:["The external boundary matters here. If the workflow calls an API or receives a provisioning callback, log the event ID, client ID, location ID, configuration version, status, and response category. When a ",e.jsx(t,{to:"/fix/ghl-webhook-not-receiving-data",style:{color:"var(--tm-accent)"},children:"GHL webhook is not receiving data"}),", you should be able to determine whether GHL emitted the event, whether the receiver accepted it, and whether the consumer rejected the payload."]}),e.jsx("h2",{children:"Common variations"}),e.jsxs("ul",{children:[e.jsxs("li",{children:[e.jsx("strong",{children:"Reseller with ten client sub-accounts:"})," keep the reseller relationship in the control plane, while each client retains a separate GHL location and configuration record. Never use the resellerâs account as a shared database for all client operational data."]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Web developer handing off a finished build:"})," define a handoff state. The developer can provision and test the build, but ownership of credentials, client access, support, and future changes must be transferred explicitly."]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Referral-only partner:"})," do not grant GHL access just to record attribution. Store the partner identifier and expose only the reporting or notification path that the agreement requires."]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Different AI voice requirements per client:"})," treat the voice provider assistant ID, prompt version, transfer destination, business hours, and consent behavior as configuration values. Do not fork the whole workflow for every variation unless the behavior genuinely differs."]}),e.jsxs("li",{children:[e.jsx("strong",{children:"One client with multiple calendars or locations:"})," model the relationship explicitly instead of assuming one client equals one calendar. Booking failures often come from the wrong calendar ID or timezone, not from the partner model itself. The ",e.jsx(t,{to:"/fix/ghl-calendar-booking-failures",style:{color:"var(--tm-accent)"},children:"GHL calendar booking failure diagnostic"})," covers that downstream branch."]})]}),e.jsx("h2",{children:"When this isn't the issue"}),e.jsx("p",{children:"If onboarding is already structured but the system still feels unscalable, inspect these forks:"}),e.jsxs("ul",{children:[e.jsxs("li",{children:[e.jsx("strong",{children:"The workflow is firing but the client does not see the result:"})," inspect the downstream action, user permissions, notification settings, and calendar or pipeline identifiers. A trigger problem and an action problem require different fixes."]}),e.jsxs("li",{children:[e.jsx("strong",{children:"The same client is provisioned twice:"}
10)," add an idempotency key based on the internal client ID and configuration version. A timestamp is not an idempotency key because every retry produces a new value."]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Partners keep requesting custom exceptions:"})," move the variation into a typed configuration field only when it is a repeatable capability. Keep one-off behavior as a documented exception rather than creating another permanent workflow branch."]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Support is still doing every test call:"})," add a self-serve test action with a recorded result, but keep production activation gated on the required checks. Automation should remove repetitive verification, not remove the ownership boundary."]})]}),e.jsxs("p",{children:["Stuck on this for more than 30 minutes? ",e.jsx(t,{to:"/contact#booking",style:{color:"var(--tm-accent)"},children:"Book a 30-min scope call"}),". I'll get on Zoom, look at your GHL partner and onboarding setup, and either fix it on the call or scope what it'll take. No upsell, no decks. You can also ",e.jsx("a",{href:"mailto:[email protected]",style:{color:"var(--tm-accent)"},children:"email Basit"}),"."]}),e.jsx("h2",{children:"Related pages"}),e.jsxs("ul",{children:[e.jsx("li",{children:e.jsx(t,{to:"/fix/ghl-workflow-not-triggering",style:{color:"var(--tm-accent)"},children:"Fix: GoHighLevel workflow not triggering"})}),e.jsx("li",{children:e.jsx(t,{to:"/fix/ghl-webhook-not-receiving-data",style:{color:"var(--tm-accent)"},children:"Fix: GoHighLevel webhook not receiving data"})}),e.jsx("li",{children:e.jsx(t,{to:"/fix/ghl-calendar-booking-failures",style:{color:"var(--tm-accent)"},children:"Fix: GoHighLevel calendar booking failures"})})]})]}),")"]}),u=()=>e.jsx(n,{tier:3,slug:"ghl-at-scale",title:"GoHighLevel at scale: partner onboarding, referrals, and client isolation â basitq.cloud",subtitle:"Adding partners is straightforward; making every partner client predictable is the real GoHighLevel architecture problem. I use account boundaries, explicit ownership, and event-driven onboarding to keep support work from growing with every signup.",metaDescription:"GoHighLevel at scale: design partner onboarding, referrals, sub-account access, and client workflows without repeating manual work for every new client.",lastUpdated:"2026-09-21",readTime:"7",techStack:["GoHighLevel","Webhooks","n8n","Supabase","VAPI"],relatedPages:[{title:"Ghl Workflow Not Triggering",slug:"ghl-workflow-not-triggering",tier:1},{title:"Ghl Webhook Not Receiving Data",slug:"ghl-webhook-not-receiving-data",tier:1},{title:"Ghl Calendar Booking Failures",slug:"ghl-calendar-booking-failures",tier:1}],relatedServices:[],faq:[{q:"Should a reseller manage one GoHighLevel sub-account or multiple client accounts?",a:"A reseller should manage only the sub-accounts they are responsible for. Keep the clientâs operational data and credentials inside that sub-account, and define which actions the reseller can perform before onboarding."},{q:"How should referrals be tracked in GoHighLevel?",a:"Capture the referral source at the first known contact event, store a normalized partner identifier, and carry it through opportunity creation and client onboarding. Do not rely on a mutable contact tag as the only source of attribution."},{q:"What is the biggest bottleneck when adding GoHighLevel partners?",a:"The bottleneck is usually exception handling: different permissions, onboarding fields, phone numbers, calendars, prompts, and workflow branches. A versioned configuration contract exposes those differences before automation starts."},{q:"Can one GoHighLevel snapshot solve multi-client onboarding?",a:"A snapshot can standardize the starting configuration, but it does not solve client-specific credentials, numbers, calendars, prompts, consent, referral attribution, or operational ownership. Those values need a separate onboarding layer."}],body:r});export{u as default};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.