Skip to content

Integrations

HighLevel SDK: get started

The complete HighLevel API through typed application or agent clients

Use an ordinary App, or an Agent when the workload needs agent identity. Alter builds provider requests from a reviewed contract and applies the same connection authorization, policy, approvals, and audit records as raw requests.

Terminal window
npm install @alter-ai/sdk @alter-ai/gohighlevel
# Python (installs core automatically)
pip install alter-sdk-gohighlevel

The provider package includes only HighLevel types and response schemas and pins its contract version.

Connect HighLevel either way: a Marketplace app through OAuth, or a Private Integration Token stored as a managed secret. Request the scopes the application needs and retain the grant ID. The same client works on both grants. Alter supplies the credential and the required Version: v3 header; neither belongs in operation input.

import { App } from "@alter-ai/sdk";
import { HighLevelIntegration } from "@alter-ai/gohighlevel";
const app = new App({ apiKey: process.env.ALTER_API_KEY! });
try {
const client = new HighLevelIntegration(app, { grantId: process.env.GOHIGHLEVEL_GRANT_ID! });
const result = await (await client.customMenus.getCustomMenus({})).wait();
console.log(result.bodyJson());
} finally {
await app.close();
}
from alter_sdk import App
from alter_sdk_gohighlevel import HighLevelIntegration
async def first_call(api_key: str, grant_id: str):
async with App(api_key=api_key) as app:
client = HighLevelIntegration(app, {"grant_id": grant_id})
call = await client.custom_menus.get_custom_menus({})
result = await call.wait()
return result.body_json()

Contract 2026-10-31 publishes 610 of the 627 operations in HighLevel’s 42 official v3 OpenAPI documents, pinned at one commit. The policy catalog names all 627. Of the other seventeen, eight no connection can call and nine are sensitive admin operations that are never SDK methods; see Operations not published. Every published operation sends Version: v3. The API reference links every operation to its complete input fields, response models, TypeScript and Python examples, and policy identity. Browse by resource under this provider’s API reference in the Integrations sidebar, or download the pinned OpenAPI specification. The Developer Portal provider view and alter integrations operations gohighlevel --version 2026-10-31 also expose the schemas.

Generated calls pin the Alter contract version. HighLevel’s own product, role, scope and subscription requirements still apply. There is no silent version fallback and no automatic write retry or pagination.

Terminal window
alter integrations execute gohighlevel gohighlevel.get_custom_menus \
--version 2026-10-31 --grant "$GOHIGHLEVEL_GRANT_ID" --input '{}' --wait

Arguments use native path, query, headers, and body containers. Array query parameters are sent the way the provider’s document declares them: one delimited value where it declares explode: false (or a pipe- or space-delimited style), repeated keys otherwise. SDK methods preserve provider field names, decimal lexemes and large integers as strings when necessary. HTTP 204 returns null / None from the typed decoder. Binary responses use bodyBytes() / body_bytes(); the CLI can save them with --output-file.

Operations in HighLevel’s website but not in this contract

Section titled “Operations in HighLevel’s website but not in this contract”

HighLevel’s website documents about 217 newer operations (projects, courses, ClientPortal AI, more of the affiliate manager, workflows and others) that it has not published in a machine-readable document. Alter generates from the published documents only, so those operations are not in this contract: they are not SDK methods and the policy catalog does not name them. A raw proxy call to one is unclassified, so a rule that names operations or families refuses it.

A raw proxy call carries the Version header its caller sends; Alter injects only the credential there.

A policy rule can require approval for any operation. The call returns a pending approval; wait() resolves once the approver decides, and resume(approvalId) picks a pending approval up after a restart. Approval cards show the operation, its contract version and the request input the sensitivity review marked safe to display.

Five operations upload a file: conversations.upload_file_attachments, forms.upload_to_custom_fields, locations.upload_file_custom_fields, medias.upload_media_content and social_planner.upload_csv. Every binary input is a JSON object with filename, content_type, and content_base64; Alter sends each body field as a named multipart part. The two custom-field uploads take files under a key the caller builds, <custom_field_id>_<file_id>.

An upload a policy sends for approval is held with the pending approval, and the approver sees each file as its name, type, size and digest, never its contents. The shared proxy accepts up to 5 MiB of serialized input and encoded upload data, and base64 adds overhead.

Every operation’s policy decision (its families, whether it returns a credential, its sensitive request fields and the parameters a rule can read) was reviewed against HighLevel’s page for that operation, and a new operation without a review fails generation. The per-operation families are in the HighLevel connection’s policy table. Operation names in stored rules are the names the raw proxy’s catalog has always used.

  • read is only for an operation that changes nothing, including a POST that only searches. Every other operation carries write, and a removal, void or cancellation also carries delete.
  • payment marks anything that bills, charges or changes what anyone pays.
  • send marks a call that itself delivers something to a person.
  • admin marks sub-accounts, permissions, users, custom fields, payment providers, agents and other configuration.

Sensitive request fields are allowed. They are held encrypted with a pending approval, shown masked and kept out of logs: a user’s password, third-party secrets (apiKey, authToken, authenticationValue), bank details, dates of birth and invoice taxId values.

Payload capture follows the application’s capture setting, for SDK and raw calls alike. A captured body has every field the sensitivity review marks masked; a call that can hand over a credential is never captured.

Calls that return, create or set a credential, or revoke the connection’s own, are refused by default on every connection with credential_minting_refused, before any call reaches HighLevel. A raw proxy call to the same route gets the same refusal.

OperationWhy it is refused
gohighlevel.update_userOnly when the body sets password (a sign-in password) or grants privilege or access (role, type, permissions, scopes, scopesAssignedToOnly, locationIds). An update of only profile fields runs.
gohighlevel.uninstall_applicationIt revokes the credential the connection holds; disconnect through Alter instead.
fetch-config (raw only)It returns the custom payment provider’s live and test apiKey and publishableKey.
create-config (raw only)It sets those keys.
get-location-access-token (raw only)It returns a new sub-account access token and refresh token.
create-user (raw only)It creates a user with a sign-in password the caller sets.
voice-ai.create-action, get-action, voice-ai.update-action, voice-ai.get-agent, getCallLog (raw only)Each response returns a Voice AI action’s authenticationValue, the token or key HighLevel sends to the action’s external API.
migrateConnection, get-access-token (raw only)They set an app’s external authentication credentials, or exchange the app’s client secret for tokens.

An organization admin who needs one allows it with a credential_minting rule naming provider gohighlevel and the operation, outright or behind an approval; see Allow an operation that hands over a credential. Third-party secrets a request sends for HighLevel to use and HighLevel does not return (a location’s Twilio authToken or Mailgun apiKey) grant nothing in HighLevel, so they stay allowed and masked.

Seventeen operations in HighLevel’s documents are not SDK methods. The policy catalog still names each, so a raw call classifies.

OperationWhy
POST /conversations/messages/review-reply, POST /conversations/messages/upload/initiate, POST /conversations/messages/upload/completeHighLevel marks them internal-only. conversations.upload_file_attachments is the supported upload.
DELETE /locations/{locationId}, POST /marketplace/external-auth/migration, GET /saas-api/public-api/companies/{companyId}/locations/{locationId}/wallet-balance, POST /saas/allow-attach-rebilling/{locationId}Each needs a scope HighLevel grants to no token.
POST /oauth/tokenThe token exchange takes the app’s client secret, which no connection holds, and returns tokens.
GET /payments/custom-provider/connect, POST /payments/custom-provider/connect, POST /oauth/location-token, POST /users/, POST /voice-ai/actions, GET and PUT /voice-ai/actions/{actionId}, GET /voice-ai/agents/{agentId}, GET /voice-ai/dashboard/call-logs/{callId}Sensitive admin operations: each returns or sets a credential (the custom payment provider’s keys, a sub-account token, a new user’s sign-in password, a Voice AI action’s external API token). A managed SDK never publishes one; a raw call is refused unless an allowance names it.

Eight published operations admit only an OAuth access token, so they work on a Marketplace app grant and not on a Private Integration Token: marketplace.get_installer_details, marketplace.get_rebilling_config_for_app, marketplace.get_charges, marketplace.charge, marketplace.has_funds, marketplace.delete_charge, marketplace.get_specific_charge and oauth.get_installed_location. The token’s policy catalog leaves them out, so a rule that names operations or families refuses them, and HighLevel refuses the token in any case. POST /oauth/location-token is also OAuth-only, and is refused by name on both connections.

HighLevel reports most errors as 4xx and 5xx responses. 158 published operations also document a top-level outcome boolean in their 2xx body (success, succeded, succeeded or status, most described as “Success or Failure” or “Status of api action”). When it is false, the call failed: the result has ok false and bodyJson() / body_json() raises ProviderAPIError, while bodyBytes() / body_bytes() still return HighLevel’s body. Seven bulk operations report per-item results beside the boolean, so a partial result is returned as a typed body, never a fault: contacts.add_remove_contact_from_business, contacts.bulk_update_tags, products.bulk_edit, products.bulk_update, products.bulk_update_product_review, saas.bulk_enable_saas_deprecated and social_planner.bulk_delete_social_planner_posts.

The pinned official source, its hash, the generated methods and the policy mapping are checked together. Scheduled checks save upstream candidates and report additions, removals and schema changes; they never replace a published contract automatically. Existing contract versions remain available for installed SDKs and pending approvals.

Persist call.approvalId (TypeScript) or call.approval_id (Python), then construct a fresh provider client and call resume(savedId), followed by wait(). The stored approval selects the operation; this installed package verifies its provider and contract version before attaching the response validator. Mismatches raise ManagedOperationError, never silently decode using another contract. See persist-and-resume examples.

Policy operation fields also accept the published SDK function IDs listed by alter integrations operations gohighlevel. Use when.operation for a json_match or conditional require_approval rule, or match.operations for content_match (including redaction and step-up). Conditional approval can also use when.match.operations with parameter conditions. alter policy simulate --operation <function-id> tests that exact function.

Managed calls carry a server-verified function identity. Alter still evaluates policy against the actual method, URL and body after building the request from the authorized connection. Existing endpoint, method, host, catalog-operation and family rules continue to apply unchanged. A raw call can share an endpoint with several SDK functions and matches all of them; a function name never grants permission or bypasses existing restrictions.

For example, this rule requires approval for one function:

{
"effect": "require_approval",
"when": { "operation": "gohighlevel.send_a_new_message" },
"approval": { "approvers": ["reviewer@example.com"] }
}

Save it with alter policy rules create --app <app-id> --grant <grant-id> --type require_approval --body @rule.json, or select the function in the portal.

Report an issue with this page

Necessary

Required for sign-in, security, authorization, and remembering your choices.

Always active

Analytics

Helps us understand which product and documentation features are useful.

Performance diagnostics

Uses performance tracing and privacy-masked session replay to diagnose problems.

You can change these choices at any time from Cookie settings.