GoHighLevel MCP for Clinics: Locking Down Contact Data
Lock down GoHighLevel MCP in a clinic: one sub-account per token, no contact or conversation scopes, counts instead of records, campaign questions sent to Curve.
You lock down GoHighLevel MCP in a clinic by connecting one sub-account per Private Integration Token, granting no contact, conversation, appointment, form or opportunity scopes, and sending marketing questions to Curve, which answers them as rounded weekly campaign counts instead of patient records. The catch is that HighLevel's scopes are coarse: the read scope that lists your pipelines also returns opportunities with each contact's name, email and phone attached. HighLevel's BAA covers HighLevel, not the AI vendor that receives the tool results, while Curve signs a BAA on every plan and keeps contact records out of its MCP answers.
What a clinic GoHighLevel connection can reach
HighLevel runs two MCP endpoints, and they fail in different ways.
- The original endpoint (
services.leadconnectorhq.com/mcp/) authenticates with a Private Integration Token created inside one sub-account, plus alocationIdheader. It exposes named tools such ascontacts_get-contacts,conversations_get-messages,calendars_get-appointment-notesandconversations_send-a-new-message. - The v2 endpoint for Claude (
/mcp/anthropic/v2) signs in through LeadConnector's OAuth consent flow and exposes a handful of generic tools. HighLevel's developer docs list four (list_locations,search_operations,describe_operation,execute_operation). Its Claude help article lists six, addingsearchandfetch, which find and retrieve records "by name, email, phone, tag." An agency can authorize one sub-account, several, or all of them in a single grant.
HighLevel's help article counts 625 operations behind those tools across 40 product domains, and its developer docs say more than 550 across 38. Either way, far more than the tool list suggests. You cannot switch off "the contacts tool" because there isn't one. The only real controls are which sub-accounts a connection reaches and which scopes it holds.
HighLevel gates irreversible and destructive operations behind extra confirmation. Its documentation describes no comparable gate on reads, and reads are where the HIPAA exposure sits: every record a tool returns lands in the model's context and travels to the AI vendor. The compliance verdict is in Is the GoHighLevel MCP HIPAA Compliant? This guide is the operator's version.
Scope each connection to one sub-account and the fewest scopes
HIPAA's minimum necessary standard (45 CFR 164.502(b)) requires reasonable efforts to limit PHI to the minimum necessary for the purpose. Marketing analysis is not treatment, so the treatment exception does not apply. A token holding contacts.readonly to answer a question about blog traffic fails that test on its face.
Use the method where you pick the scopes
- OAuth on v2. The default individual-user mode ties the connection to one location for its lifetime and runs each request on a short-lived token scoped to that location, so nobody stores a secret by hand. HighLevel also says OAuth offers a broader scope set than Private Integration Tokens. HighLevel describes the consent screen as "review and approve" and does not document unticking individual scopes. If it lists contacts, conversations or opportunities and you cannot remove them, decline.
- Private Integration Token. You tick each scope yourself when you create it inside one sub-account, so it reaches one location by construction. The cost is a bearer secret, and HighLevel's setup example pastes it in plain text into the client's JSON config. In Claude Code that file can be a project's
.mcp.json, onegit addaway from a repository.
In a sub-account that holds patients, the allowlist below is only enforceable where you pick scopes yourself, which HighLevel documents for Private Integration Tokens. Use a token, keep it out of the config file, and rotate it on a date. Claude Code expands ${VAR} in .mcp.json headers, so "Authorization": "Bearer ${GHL_PIT}" keeps the secret in your environment instead of the file. HighLevel itself advises keeping tokens in secure storage and replacing them regularly.
The scope allowlist for a clinic sub-account
Reasonable to grant, because these describe configuration and content rather than people:
locations.readonly(View Locations) andlocations/customFields.readonly(View Custom Fields): sub-account details and custom field definitions, meaning the field names, not the values stored on contacts.locations/tags.readonly: the tag list itself, without the contacts wearing the tags.calendars.readonly(View Calendars): calendar setup and free slots.- Blog, social planner or email template write scopes, only if content work is the reason for the connection. HighLevel's own advice is to "grant edit permissions only when the AI agent needs to create or update data."
Withhold in any sub-account that holds patients:
contacts.readonly(View Contacts) andcontacts.write(Edit Contacts).conversations.readonly(View Conversations) andconversations/message.readonly(View Conversation Messages), plus their edit counterparts. Withhold both. Conversation search alone returns each thread's last message body next to the contact's name, email and phone, and the message scope also returns call recordings and transcriptions.calendars/events.readonly(View Calendar Events), which returns appointments and appointment notes.forms.readonly(View Forms), which returns submissions as well as form definitions. Treat survey, payment and invoice scopes the same way.opportunities.readonly(View Opportunities), for reasons the pipeline section below explains.
One uncomfortable detail: the recommended-scopes list in HighLevel's MCP setup article starts with View Contacts, Edit Contacts, View Conversations and Edit Conversations. Of the 17 permissions that list recommends, three survive a clinic allowlist: View Calendars, View Custom Fields and View Locations. The list suits a business whose contacts are not patients. For lifetimes and rotation across every connector, see MCP Access Tokens for Health Data.
Keep contact and conversation tools out of the session
The conversation log is the most sensitive object in a clinic's GoHighLevel account. SMS threads, email bodies, call recordings and web chat hold what patients type when they think they are talking to the front desk: medication names, refill questions, symptoms, insurance details. Pull one thread through conversations_get-messages and the phone number, the message text and the clinic's identity travel together to the AI vendor. That is an identifiable person, health information and a provider relationship in one tool result.
Withholding the conversation scopes also removes two operator hazards that contact reads do not carry. conversations_send-a-new-message sends into a live thread, so an agent that misreads "draft a follow-up" can text a patient from the clinic's number. And every inbound message is text written by a stranger, landing in a session that may hold write scopes, which is the indirect prompt injection pattern OWASP describes.
Tags need the same care. A tag list is configuration, but a tag such as glp1-consult-booked on a named contact pairs an identity with a treatment, and contacts_add-tags writes exactly that pairing. If you need tag totals, count them where the data lives. MCP Tool Calls and PHI maps the other places tool arguments and results leak.
Ask for pipeline counts, not contact records
Most people who want GoHighLevel in Claude want pipeline answers: new leads this week, booked consults, no-shows. All counts. The trap is that the count lives inside a search built to return records: the response carries a total, and next to it a page of opportunities whose size is whatever the model asks for.
- Get Pipelines returns pipeline and stage structure. No contacts.
- Search Opportunities returns that total plus the records, and each record carries a nested
contactobject withname,email,phoneandtags. The request can also pull the contact's notes, tasks and calendar events. - Both sit behind one scope,
opportunities.readonly. You cannot grant pipeline structure without also granting opportunity search.
Stage names make it worse. Intake teams often name stages after treatments ("Semaglutide consult booked"), so an opportunity record is a named person sitting in a treatment-named stage. The opportunity name is free text too, and it is often just the patient's name.
"Only give me the count" is not a control
Telling Claude to return only the total changes what the model shows you. It does not change what the tool returned. By the time the model writes "37 booked consults," the records behind that number are already in its context and in the vendor's systems. An output instruction is a presentation choice, not a data boundary.
That leaves two honest ways to get pipeline counts in an account that holds PHI:
- Count inside HighLevel. Use its own dashboards and reports, so the records never leave the platform that already holds them.
- Send the milestone out as an event, not a record. A HighLevel workflow's Custom Webhook action can post the stage change to Curve's incoming webhook, which matches it to the ad click by email, click ID or bridge token and counts it against the campaign under a neutral name. That is the pattern in CRM lifecycle stages as conversions, and it moves the question off the CRM entirely.
How Curve takes marketing questions off the CRM
Look at what a CRM connection usually gets asked: which campaign produced the most conversions over the last four weeks, what a conversion cost per campaign over the last 13 weeks, whether a campaign launched a month ago is converting. None of those needs a patient. They need campaign spend joined to conversion counts. The clinic's own team asks them through Curve MCP, and agency staff answer the same questions in Curve Analyst inside the client's account.
For a HighLevel clinic, the path into Curve runs like this:
- The server-side tracking script captures the ad click when the visitor lands on the clinic's site.
- When an opportunity reaches a stage such as consult booked, a HighLevel workflow's Custom Webhook action posts the event to the incoming webhook.
- The event is matched to the click by email, click ID or bridge token. Incoming data cannot overwrite protected attribution and contact fields.
- It becomes a conversion under a neutral name, the only name any ad platform it is forwarded to will see.
- The MCP counts it in that campaign's conversions and cost per conversion. Those figures cover everyone attributed to the campaign across all conversion types, not one pipeline stage at a time.
What Curve MCP returns, and what it never does:
- Returns: organization-level KPIs and a reconciliation of your server-side campaigns: each campaign's platform-reported spend, clicks and impressions next to the conversions Curve's server-side tracking recorded, with cost per conversion, for completed weeks over fixed look-backs (last week, or the last 4, 13 or 52 weeks). One link opens the full sent, accepted and matched view inside Curve.
- Never returns: contact records, revenue or ROAS, custom date ranges, or channel, device, region or page breakdowns. Small groups are withheld, figures are rounded, and unapproved campaign names read "(label hidden)".
- Controls: read-only tools, every call logged, and a final guard that blocks any answer containing something shaped like an email, phone number, ID, date or name.
Curve MCP access belongs to the clinic's own users. An agency login can revoke a client's MCP access or switch it off, but cannot switch it on or hold an MCP grant in a client's account, the opposite of a HighLevel all-sub-accounts grant. Person-level questions get a single-use link into Curve Analyst that needs a login and carries no data. The MCP works with anything that can connect to an MCP server: Claude (desktop, web and Claude Code), ChatGPT, Cursor and any other MCP-capable client or agent. The AI vendor stays the clinic's choice and contract, outside the tracking layer's BAA.
The agency multi-account problem
The risk case against all-sub-accounts grants (one chat holding PHI from several clinics, and a HIPAA toggle that is set per sub-account) is made in Is the GoHighLevel MCP HIPAA Compliant? The operating rules that follow from it:
- One clinic per connection. Authorize a single sub-account per grant or token, and keep one AI workspace per client, so nothing read from one clinic is still in context when someone asks about the next.
- Check the toggle before you connect. Confirm HIPAA is switched on in the sub-account's Advanced Settings before any connection reaches it, not after.
- Revoke in offboarding. Put it on every offboarding checklist: review or revoke OAuth access from account settings, and delete Private Integration Tokens in the sub-account that issued them.
MCP for Healthcare Agencies covers the wider boundary model.
Frequently asked questions
Can Claude read my pipelines without seeing contacts?
No. Get Pipelines and Search Opportunities share opportunities.readonly, and search results carry each contact's name, email, phone and tags. If stage structure is all you need, read it inside HighLevel rather than granting the scope.
Which HighLevel scopes are safe to grant in a clinic sub-account?
View Locations, View Custom Fields, View Calendars and the tag list, plus blog, social planner or email template write scopes when content work is the point, and nothing that returns contacts, conversations, calendar events, forms, payments or opportunities.
Is a read-only token safe enough?
No. Read-only removes the write risks: texts to patients, edited tags, changed opportunities. It does nothing about exposure, because reading is how PHI reaches the model. A token holding only conversations.readonly still returns message bodies next to phone numbers.
Can I just tell Claude to return counts only?
You can, and it changes the answer you see, not the data the tool returned. The records are in the model's context before it summarizes them. A count is only safe when it is computed before the data leaves the system that holds it.
What can the GoHighLevel MCP safely do in a clinic account?
Work that never touches a person: blog and social drafts, email templates, and audits of custom field definitions and tag names. Content work needs the blog, social planner or email-template write scopes. Grant those and nothing else, and have a human publish anything that goes live under the clinic's name.
Does Curve MCP replace HighLevel reporting?
No. HighLevel stays the system for patient-facing operations, inside a HIPAA-enabled sub-account. The MCP answers the campaign layer: spend, clicks, conversions and cost per conversion by completed week, with small groups withheld and no contact data in any answer.
Where to start
- Inventory every HighLevel MCP connection and Private Integration Token across your sub-accounts, and revoke any grant that spans more than one clinic.
- Rebuild the survivors as one Private Integration Token per sub-account using the allowlist above, read from an environment variable, with a rotation date on each.
- Move pipeline and campaign questions out of the CRM: post stage changes to Curve with a workflow Custom Webhook action and ask the campaign question there.
- Run the free compliance scanner on each clinic's website, because the CRM is rarely the only place patient data leaks.
To see campaign questions answered from a clinic's own data with these privacy rules applied, book a Curve MCP demo, or start at curvecompliance.com for the server-side tracking layer that feeds it.
Reviewed September 2026. HighLevel endpoints, tools, scopes, multi-account behavior and HIPAA add-on terms checked against HighLevel's help center articles and published API reference.
Stay Compliant. Scale Confidently.
Join healthcare innovators who trust Curve for HIPAA-compliant ad tracking.Launch in hours, not months. Your growth stack, now HIPAA-safe.
Book a free tracking audit