---
name: highlevel-oauth-app
description: Creates or signs in to a HighLevel (GoHighLevel / LeadConnector) developer marketplace account and registers a marketplace app to obtain OAuth2 client ID and client secret — with the irreversible Agency-vs-Sub-Account target-user decision, redirect URL, scopes, private-app install cap, token and Version-header behaviour, and a safe credential handoff. Use when asked to get HighLevel OAuth credentials, set up a HighLevel developer account, create a GoHighLevel marketplace app, rotate a HighLevel client secret, publish or review a HighLevel app, or fix a HighLevel install error like a blocked install, a missing locationId, or 401s on sub-account endpoints. For any other vendor's developer portal, use that vendor's skill instead.
---

# HighLevel OAuth2 App Registration

Get a working HighLevel (GoHighLevel / LeadConnector) OAuth2 client — a developer marketplace account, an app,
a redirect URL, scopes, client ID and secret — for a platform that connects many customers' HighLevel accounts.

The form-filling is easy. Three things are not. **Target User (Agency vs Sub-Account) is chosen at app creation
and cannot be changed afterwards** (§3) — pick wrong and you rebuild the connect flow under a new client ID.
**A Private app is capped at 5 agencies** (§8) — install six and the sixth customer simply cannot install.
And **most HighLevel material on the internet is about API v1**, which reached end-of-support on 31 December 2025
and used a static API key; v2 is OAuth, and a v1-shaped runbook will send you down a dead path (§ Platform state).

## Inputs to collect before you start

Ask in one batch. Never invent.

| Input | Notes |
| --- | --- |
| **App name** | Unique across the marketplace; shown on the OAuth consent screen and in the listing |
| **HighLevel account / registration email** | The agency account that will own the developer profile (§2) |
| **Redirect URL(s)** | Every callback host your platform serves — read §5 first, the field may take only one |
| **Target User: Agency or Sub-Account** | Irreversible. §3. For a multi-tenant connector the answer is normally Sub-Account |
| **Scope set** | The scopes the connector actually sends (§6) |
| **Public or Private app** | Private caps you at 5 agencies (§8) |
| **Logo, description, screenshots, support email/phone, demo video** | Needed for a Public listing review (§8) |
| **New app or edit to an existing one?** | See §1 — the default answer is "existing" |

## Quick Start

1. Confirm a **new app** is actually needed — live customer connections are bound to the current client ID (§1).
2. Sign in at the developer marketplace, or have an agency admin create the developer profile (§2).
3. Create a **sandbox (App Test) account** — it is provisioned immediately and keeps testing off real data (§2).
4. Decide **Target User** and **Who can install** before clicking Create. Target User cannot be changed later (§3).
5. Create the app: name, type Private (for now), Target User, Who can install, bulk install (§4).
6. Set the **Redirect URL** exactly, and find out whether the pane accepts more than one (§5).
7. Select scopes on the app; they must cover everything the authorize request sends (§6).
8. Generate the **Client Keys** and copy the client secret immediately — it is shown once (§7).
9. Note the install cap and plan the public listing or security review (§8).
10. Verify with a real install → callback → token → refresh → one read call, from a sandbox (§10).
11. Hand the credentials over — never commit them (§11).

## Platform state (verified 2026-09-20 — re-verify before trusting)

Nearly everything below changed in the last two years. Check the developer changelog before following any older
write-up, including cached knowledge, a blog post or an internal doc:

- **API v1 reached end-of-support on 31 December 2025.** Existing v1 integrations keep working with no support or
  updates, and the ability to generate new API keys is being removed from Agency and Sub-account settings. v1 used a
  static per-account API key with no OAuth at all — which is what most third-party tutorials, YouTube walkthroughs
  and older automation docs still describe. **v2 requires OAuth 2.0** for a marketplace app (or a Private Integration
  Token for a single sub-account; see §7).
- **Every v2 API call carries a `Version` request header.** Two schemes coexist: date-based (`2021-04-15`,
  `2021-07-28`, `2023-02-21`) and named, starting with **`v3`, released 11 June 2026**. All are listed as supported,
  retirement dates "TBD". A retired version stops being accepted, so a pinned header is a dated liability.
- **Private apps created on or after 18 November 2025 are capped at 5 agencies.** At 6+, new installs are blocked
  ("Install unavailable. Please contact the app developer."); existing installs keep working. Sub-accounts do not
  count separately — one agency is one count no matter how many sub-accounts, and bulk installs count once.
- **App versioning is live**: Draft → In Review → Live → Disapproved/Deprecated, one Draft at a time, at most 5
  versions per app. Install limits apply at the **app** level, not per version.
- **The OAuth consent screen lists every requested scope** with a plain-language explanation, and shows an explicit
  warning for sensitive scopes such as user creation/modification. It applies to new installs and re-authorizations,
  on both the standard and grey-labeled (white-label) install links.
- **Webhook signing is mid-migration**: the legacy RSA `X-WH-Signature` header **is deprecated on 1 September 2026**;
  the current header is Ed25519 `X-GHL-Signature`. If you consume webhooks, verify the new one (§9).
- **Sandbox accounts are self-serve and immediate**, but limited: 2 sub-accounts per sandbox, sandbox Private
  Integration Tokens are throttled to roughly 25 requests / 10 seconds and 10,000/day, accounts are temporary (on the
  order of months) and data may be reset or purged without notice.
- **The changelog carries "api path removed without deprecation" entries**, repeatedly and recently. Treat HighLevel
  endpoints as less stable than the docs' tone suggests, and subscribe someone to the changelog.

If the developer portal does not look like this, stop and report what you actually see rather than clicking on.

## 1. Decide: reuse the existing app, or register a new one

**A new app means a new client ID, and every existing customer connection is bound to the old one.** Every customer
would have to re-install. Reuse the existing app (edit in place, or clone it as a new Draft version) for: adding a
scope, changing the redirect URL, publishing publicly, or diagnosing an install failure.

Register a **new** app only when the user explicitly wants one: a replacement for a compromised app, a second brand
or region, or — the one case where you have no choice — **a change of Target User** (§3), which the platform does not
allow on an existing app. Say which path you are taking before you touch anything.

## 2. Account and sandbox

- The developer marketplace lives at **`https://marketplace.gohighlevel.com`**; sign in or sign up there. An agency
  admin who picks "Sell on Marketplace" in their HighLevel account gets a developer profile created automatically
  from their existing details.
- Apps are owned by the developer profile behind that account. For a platform integration, use a shared or
  service-owned account the team controls — not a person who might leave. Record whose account it is.
- **Sandbox (App Test) account** — created from the developer portal with **+ Create App Test Account**, provisioned
  immediately, works as a standalone HighLevel account. Use one for install testing so nothing touches a customer's
  live sub-account. Respect the fair-use limits in **Platform state**; it is not a staging environment for real work.

Hand control back to the user, saying exactly what is needed, for anything only they can do: signup email
verification, 2FA, accepting developer terms, payment details on the agency account. Do not retry a blocked step in
a loop. Before accepting developer or marketplace terms, skim for anything a founder would want flagged — revenue
share, exclusivity, data-use commitments, reselling restrictions.

**If this session has no browser automation** (the usual case for a CLI or cloud run), do not pretend to click. Hand
the user an exact, ordered click path with the literal values to paste (§5 redirect URL, §6 scope list), then
continue once they report back with the client ID.

## 3. Agency vs Sub-Account — the decision that defines the app

This is the HighLevel concept everything else hangs off, and the one that is expensive to get wrong.

HighLevel accounts nest: an **Agency** (also called Company) owns many **Sub-Accounts** (formerly, and still in the
API, "Locations"). An access token is scoped to **one or the other**, and the token response says which:
`userType: "Company"` with a `companyId`, or `userType: "Location"` with both a `locationId` and a `companyId`.
Sub-account endpoints — contacts, opportunities, businesses, forms, tags — want a Location token. An agency token
called against them does not quietly return a filtered view; it fails.

Three fields on the app decide what you get:

| Field | Values | Notes |
| --- | --- | --- |
| **Target User** | Agency / Sub-account | **Cannot be modified once set.** HighLevel's own guidance: roughly 95% of apps should be Sub-account |
| **Who can install** | Both Agency & Sub-account / Agency only | Controls marketplace visibility and who may press install |
| **Bulk installation** | Yes / No | Mandatory "Yes" for new marketplace apps; lets an agency install across many sub-accounts at once |

What comes back at install time:

- **Sub-account target, sub-account admin installs** → a **Location** token. Usable immediately. This is the happy path.
- **Sub-account target, agency admin installs (including bulk)** → a **Company** token, flagged as a bulk
  installation. It carries no `locationId`. To touch sub-account data you must exchange it, per sub-account, at
  `POST https://services.leadconnectorhq.com/oauth/locationToken` — form-encoded `companyId` + `locationId`, the
  agency token as the bearer, and the `Version` header. That returns a Location token with its own refresh token.
- **Agency target** → a Company token for agency-level work (creating sub-accounts, SaaS configuration, agency users).

**For a multi-tenant platform whose unit of connection is one customer CRM: Target User = Sub-account, Who can
install = Both Agency & Sub-account.** That gives a Location token directly on the common path, one connection per
sub-account, per-sub-account rate limits (§8), and a consent screen the sub-account admin understands.

But "Both" means **you will also receive Company tokens**, from every agency-side or bulk install. A connector that
assumes a `locationId` is present stores a connection that authenticates but reads nothing. Handle it: on a Company
token, discover the installed sub-accounts (the installed-locations endpoint, or the `AppInstall` webhook, which
carries `locationId`, `companyId` and `installType`) and mint a Location token per sub-account. If the platform
cannot do that yet, keep "Both" anyway — restricting to Agency only would make agency installs the *only* kind —
and say plainly in your summary that agency-initiated and bulk installs are unsupported until the exchange is
implemented, so support recognises the failure when it arrives.

Choosing **Agency** as Target User to "get everything at once" is the classic mistake: it makes every connection
agency-wide, changes the consent audience, needs a token exchange for every ordinary read anyway — and it cannot be
undone without a new app and a re-install by every customer.

## 4. Create the app

In the developer portal: **My Apps → Create App**. The fields that carry weight:

1. **App name** — unique, and public once listed.
2. **App type** — **Private** while you build and test; **Public** is listed in the marketplace and installable by
   anyone *after approval*. HighLevel's own advice is to start Private and switch when stable. Read §8 first: Private
   is capped at 5 agencies, and the cap applies to apps created on or after 18 November 2025.
3. **Target User** and **Who can install** — §3. Target User is permanent.
4. **Bulk installation** — Yes for new marketplace apps.

Then, under the app's **Advanced Settings**:

| Pane | Holds |
| --- | --- |
| **Auth** | Scopes, Redirect URL, and the **Install Link** (standard and white-label) |
| **Secrets → Client Keys** | Client ID + client secret (§7) |
| **Secrets → Shared Secret Key** | Only for signed user-context tokens in marketplace modules — not needed for a data connector |
| **Profile** | Listing content: logo, description, screenshots, support channel |
| **Versions** | Draft / In Review / Live lifecycle (§8) |
| **Webhooks** | One webhook URL for the app (§9) |

## 5. Redirect URL

The authorization code is delivered to the redirect URL, which must be **HTTPS** and must match what your OAuth flow
sends, exactly — small differences break installs and the error surfaces at connect time, not at save time.

For a platform that serves several regions, the callbacks are one path per data center:

```
https://api.unified.to/oauth/code          # us (default)
https://api-eu.unified.to/oauth/code       # eu
https://api-au.unified.to/oauth/code       # au
https://api-dev.unified.to/oauth/code      # dev
```

**Check how many the pane accepts before you promise anything.** HighLevel's app-creation guide describes entering
"your URL in the Redirect URL field", in the singular, and does not document a multi-value list; the public-listing
requirements say "redirect URLs configured", in the plural. Open the Auth pane and look. If it takes only one, a
multi-region platform needs **one app — and therefore one client ID and secret — per callback host**, which is a
materially different piece of work and a different answer for the user. Report what you find; do not assume either way.

Customers on a **custom API domain** call back to their own host. Those customers bring their own HighLevel app and
credentials — do not add customer domains to the shared app.

## 6. Scopes

Scopes are selected on the app, in the Auth pane, from a dropdown; the authorize request then sends a space-delimited
`scope` parameter. **What you send must be covered by what the app declares** — and, with versioning in play, by what
the **Live** version declares, not by the Draft you just edited.

Scope strings are `<resource>.<action>` (`contacts.readonly`, `contacts.write`) with some nested forms
(`locations/tags.readonly`). Each one in HighLevel's catalogue is tagged with an access type — **Sub-Account**,
**Agency**, or both — which is the same Agency/Location split as §3: a sub-account-level scope is useless on an agency
token. `users.readonly` / `users.write` are among the few that exist at both levels. Read the scopes reference rather
than guessing a name.

A CRM-shaped connector typically needs:

| Scope | For |
| --- | --- |
| `contacts.readonly` / `contacts.write` | Contacts and leads, plus their tasks and notes |
| `opportunities.readonly` / `opportunities.write` | Opportunities (deals) and pipelines |
| `businesses.readonly` / `businesses.write` | Businesses (the company-shaped object inside a sub-account) |
| `locations/tags.readonly` / `locations/tags.write` | Sub-account tag catalogue |
| `users.readonly` / `users.write` | Users — also the cheapest identity call after connect |
| `forms.readonly` | Forms and form submissions |

Ask for the minimum. Scope justification is a **review requirement** for a public listing and for the private-app
security review, the consent screen now spells each scope out to the installing admin, and sensitive scopes (user
creation and modification in particular) raise a visible warning that costs you installs.

> **Connector facts, as of 2026-09-20 — confirm with the connector's owner before registering anything.**
> This platform's HighLevel connector **installs at the sub-account (Location) level**: it sends customers to the
> standard marketplace install link (`https://marketplace.gohighlevel.com/oauth/chooselocation`) or its white-label
> equivalent on `marketplace.leadconnectorhq.com`, and it reads the `locationId` out of the token response and scopes
> every subsequent API call to that one sub-account. It does **not** implement the agency → location token exchange,
> so a Company token from an agency-side or bulk install yields a connection with no sub-account to talk to.
> It requests, per object: `contacts.readonly`, `contacts.write`, `businesses.readonly`, `businesses.write`,
> `locations/tags.readonly`, `locations/tags.write`, `opportunities.readonly`, `opportunities.write`,
> `users.readonly`, `users.write`, `forms.readonly`, with `users.readonly` alone for the identity/login flow.
> The app it is pointed at must declare all of these on its **Live** version.
> It exchanges and refreshes with a **form-encoded POST carrying client ID and client secret in the body**, sends
> **no PKCE**, and sends **neither `user_type` nor `redirect_uri`** although HighLevel documents both on the token
> and refresh requests. It pins `Version: 2021-07-28` on every API call. It also passes one **extra query parameter
> on the install link, carrying a per-workspace "Version ID"** — HighLevel's public docs do not document a version
> parameter on the install link (`versionId` appears only in the App Install webhook payload), so treat it as
> unverified and ask the owner what it is for before copying the pattern.
> A non-OAuth path also exists: a sub-account **Private Integration Token** plus the sub-account ID.
> The connector does **not** subscribe to HighLevel webhooks today (§9).

## 7. Capture the credentials

In **Advanced Settings → Secrets → Client Keys → Add**, name the key pair and save. HighLevel generates:

- **Client ID** and **client secret**. **The secret is shown once** — copy it immediately; it cannot be re-read. The
  pane takes *named* key pairs, which is what makes a rotation without downtime possible (add the new pair, deploy,
  retire the old); confirm that in the UI before promising it, and never retire a key pair until the new one is live
  everywhere, because every token refresh runs through it.

Record alongside them:

- Install link (standard and white-label), taken from the **Show** button in the Auth pane rather than hand-built
- Token exchange and refresh: `POST https://services.leadconnectorhq.com/oauth/token`
- Agency → sub-account exchange: `POST https://services.leadconnectorhq.com/oauth/locationToken`
- API base: `https://services.leadconnectorhq.com`
- The `Version` header value the connector sends, and whether that is a deliberate choice
- Target User, Who can install, app type (Private/Public), and which app **version** is Live
- Whether these are sandbox or production credentials

**Token behaviour, and the two ways it bites:**

- **Access tokens last about 24 hours** (`expires_in` 86399/86400). Refresh; do not re-install.
- **Refresh tokens rotate.** A refresh token is valid for a year *unless used*; the moment you use it, it is invalid
  and the response carries a replacement, itself good for a year. So the new refresh token **must** be persisted on
  every refresh, and two workers refreshing the same connection concurrently will burn each other's token and leave
  the connection dead. Serialise refreshes per connection.
- HighLevel documents `user_type` (`Company` or `Location`) and `redirect_uri` on **both** the code exchange and the
  refresh. If your client omits them and it works today, that is undocumented tolerance, not a guarantee.
- The token response is where you learn what you got: `userType`, `locationId`, `companyId`, `userId`, and a bulk-install
  flag. Branch on it at connect time (§3) instead of discovering the problem on the first read call.

**Private Integration Tokens are the non-OAuth alternative, and are not a multi-tenant answer.** A sub-account admin
generates a scoped, static token in their own settings; it never expires on its own and does not auto-refresh. That
is fine for one customer's internal sync or for sandbox testing, and it is HighLevel's recommended replacement for
retired v1 API keys. It is not a way to skip app registration: every customer would have to create and paste a
password-equivalent, per sub-account, with no consent screen and no central revocation.

Report the secret once so the user can paste it into their secret store, say plainly that it is now in the transcript
and can be rotated, then move on.

## 8. Install limits, review, listing — and rate limits

- **Private app: 5 agencies.** One agency counts once regardless of sub-accounts; bulk installs count once; counts
  update in real time as installs and uninstalls happen, and the cap is per app across all versions. You get an amber
  warning at 4–5 and a hard block at 6+. **For a multi-tenant connector this is a ceiling on customers**, so raise it
  with the user before anything ships.
- **Two ways out.** Publish **Public (listed)** — Start Public (listed) review, set App type = Public, submit — or,
  only *after* you have exceeded the cap, request a **Security Review** that lifts the cap while the app stays
  Private. The security review asks for an end-to-end demo video, a video justifying every requested scope, and a
  written rationale for staying private. Material changes later (scopes, domains, data flows) can trigger a re-review,
  and new installs may be restricted until it passes.
- **Public listing review** checks branding and naming (no implying an affiliation you do not have), an accurate
  description including any costs, error-free install and uninstall, HTTPS redirect URLs, least-privilege scopes
  **with justification**, a demo video, and at least one real support channel with a 24-hour first response. No
  published turnaround — do not promise a customer a date.
- **Agencies can block you.** An agency can switch on "show only approved apps to sub-accounts" and disapprove
  individual apps; a disapproved app stops being installable **and is uninstalled from sub-accounts that already have
  it**. A connector that works at one customer and silently dies at another is usually this, and there is no API error
  that says so.
- **Rate limits: a burst of 100 requests / 10 seconds and 200,000 requests / day, counted per marketplace app per
  resource** — a resource being one sub-account or one agency. Per-resource means one noisy customer cannot throttle
  another, and it is another argument for sub-account tokens: agency-token traffic for many sub-accounts all lands in
  one bucket. Responses carry `X-RateLimit-Max`, `X-RateLimit-Remaining`, `X-RateLimit-Interval-Milliseconds`,
  `X-RateLimit-Limit-Daily` and `X-RateLimit-Daily-Remaining`; read them rather than guessing.

## 9. Webhooks (if they are in scope)

Webhooks in HighLevel are **a property of the app, not a per-sub-account subscription**: you set one webhook URL in
the app settings, select the scopes that cover the events you want, and every sub-account that installs the app
starts delivering those events. There is no per-location subscribe/unsubscribe call to make or clean up, which also
means you cannot have two apps' worth of events split by customer.

`AppInstall` is the one to wire first even if you consume nothing else — it fires on every install with `locationId`,
`companyId`, `installType`, `appId` and `versionId`, and it is how an agency/bulk install tells you which sub-accounts
you must mint Location tokens for (§3). `AppUninstall` is how you learn a connection is dead before your next 401.

Verify signatures. HighLevel sends `X-GHL-Signature` (Ed25519, current) and the legacy `X-WH-Signature` (RSA-SHA256),
**with the legacy header deprecated on 1 September 2026**; the public keys are published in the webhook integration
guide. Reject anything that fails verification.

## 10. Verify end-to-end

A registration that half-worked is worse than one that stopped early — the failure shows up later as a customer who
cannot connect. Prove it:

1. Install into a **sandbox** sub-account through your platform's real connect flow, using the credentials you just
   captured, and complete the callback.
2. Read the consent screen: the scopes listed should be exactly the ones you registered, no more.
3. Inspect the token response: `userType`, and whether `locationId` is present. **Also test the other path** — have an
   agency admin install (or bulk install) and confirm your platform either exchanges for Location tokens or fails
   loudly rather than storing a token it cannot use.
4. Force a **refresh**, then confirm the *new* refresh token was persisted and a second refresh still works.
5. Call one endpoint per object the connector maps, with the `Version` header, and confirm a write as well as a read
   if writes are supported.

| Symptom | Cause |
| --- | --- |
| Connect succeeds, every read 401s or 4xxs, no `locationId` stored | An agency installed it: you hold a Company token and never exchanged it for a Location token (§3) |
| 401 on sub-account endpoints with a token that "works" elsewhere | Agency token against sub-account endpoints — wrong token type, not a scope problem (§3) |
| Install page refuses: "Install unavailable. Please contact the app developer." | Private-app cap — 6th agency (§8) |
| App invisible to a sub-account admin | "Who can install" is Agency only, or the agency's approved-apps list excludes you (§3, §8) |
| A customer's connections die and the app disappears from their sub-accounts | The agency disapproved the app; existing installs are uninstalled (§8) |
| Scope missing at consent, although you added it | It was added to a Draft version; the Live version is what installs use (§ Platform state) |
| Redirect mismatch / code never arrives | The callback host is not the one registered on the app (§5) |
| `invalid_grant` on refresh | The rotated refresh token was not persisted, or two workers refreshed concurrently (§7) |
| Everything 401s after ~24h | The client is not refreshing; access tokens last about a day (§7) |
| Calls fail with a version error, or an endpoint behaves like a different API | Missing or stale `Version` header — the version is per request (§ Platform state) |
| 429s | Burst limit of 100/10s per app per resource; read the `X-RateLimit-*` headers (§8) |
| Webhooks stop verifying | Still checking the legacy RSA signature header after its 1 Sep 2026 deprecation (§9) |

## 11. Hand off — never commit the secret

- **Do not** write the client secret, a private integration token or a shared secret key into source control, a test,
  a fixture, a committed `.env`, a ticket, a PR body, or a chat channel. Values go to the user, for the secret store
  or console.
- If a code change is genuinely needed (a callback host, a scope list, a `Version` header value), keep it secret-free
  and say plainly what the human must set out of band.
- Close with: app name, the developer account that owns it, and **Target User / Who can install / app type**; client
  ID; where the secret was delivered; the install link, token, location-token and API base URLs; the exact scope
  strings on the Live version; the install cap and listing/review status with dates; the `Version` header in use; and
  anything left for the user to do, listed plainly.

## Stop and ask

Hand back to a human rather than guessing when: Target User would have to change (it needs a new app and a re-install
by every customer); the redirect pane turns out to accept a single URL and a multi-region platform therefore needs one
app per region; a Private app is at or near the 5-agency cap and someone must choose between publishing publicly and a
security review; a review form asks for compliance, legal, security or volume claims, or a demo video you would have
to fake; a customer's agency has blocked or must approve the app; someone proposes private integration tokens instead
of OAuth for multi-tenant use; rotating or retiring a client key pair on a live app is on the table; or the developer
portal does not match the **Platform state** section above.

When the portal behaves in a way the docs did not describe, say what you found rather than picking the option that
lets you keep going.

## References

Official HighLevel docs and support articles only; every URL below returned HTTP 200 on 2026-09-20. Note that the
docs site serves an empty shell for unknown paths, so a 200 alone does not prove a page exists — each of these was
checked for real content. The developer portal itself requires sign-in.

- Developer marketplace portal — https://marketplace.gohighlevel.com/
- OAuth 2.0 (install flow, token exchange, refresh, token types, location token) — https://marketplace.gohighlevel.com/docs/Authorization/OAuth2.0
- Scopes catalogue with Sub-Account / Agency access types — https://marketplace.gohighlevel.com/docs/Authorization/Scopes
- Access tokens for Target User = Sub-Account (agency install, `/oauth/locationToken`, bulk) — https://marketplace.gohighlevel.com/docs/Authorization/TargetUserSubAccount/
- Access token generation: agency vs sub-account scenarios — https://marketplace.gohighlevel.com/docs/Authorization/AccessTokenUseCase
- Private Integration Tokens — https://marketplace.gohighlevel.com/docs/Authorization/PrivateIntegrationsToken/
- Getting started (marketplace developer path) — https://marketplace.gohighlevel.com/docs/oauth/GettingStarted
- Create a marketplace app (app type, target user, scopes, redirect URL, client keys) — https://marketplace.gohighlevel.com/docs/oauth/CreateMarketplaceApp/
- App distribution model — https://marketplace.gohighlevel.com/docs/oauth/AppDistribution
- App testing guide — https://marketplace.gohighlevel.com/docs/oauth/AppTestingGuide
- Create a sandbox (App Test) account — https://marketplace.gohighlevel.com/docs/oauth/SandboxAccount
- Private Integration Tokens for sandbox accounts (sandbox rate limits) — https://marketplace.gohighlevel.com/docs/oauth/SandboxPIT/
- App review guidelines (public listing requirements) — https://marketplace.gohighlevel.com/docs/oauth/AppReviewGuidelines/
- How to update your app (versioning, Draft/Live, version limits) — https://marketplace.gohighlevel.com/docs/oauth/HowToUpdateYourAPP
- Policy: private app install limits and security review — https://marketplace.gohighlevel.com/docs/MarketplacePolicies/PrivateAppInstallLimits/
- Policy: sandbox fair use — https://marketplace.gohighlevel.com/docs/MarketplacePolicies/SandBoxFUP
- Rate limits and `X-RateLimit-*` headers — https://marketplace.gohighlevel.com/docs/other/rate-limits/
- API versioning and the `Version` header — https://marketplace.gohighlevel.com/docs/Versioning
- API changelog — https://marketplace.gohighlevel.com/docs/Changelog
- OAuth FAQs (token lifetimes, refresh rotation, webhook setup) — https://marketplace.gohighlevel.com/docs/oauth/Faqs
- OAuth API reference (v3) — https://marketplace.gohighlevel.com/docs/ghl/oauth/oauth-2-0-v-3
- Get location access token from an agency token — https://marketplace.gohighlevel.com/docs/ghl/oauth/get-location-access-token
- Get locations where the app is installed — https://marketplace.gohighlevel.com/docs/ghl/oauth/get-installed-location
- Webhook integration guide (signature headers and public keys) — https://marketplace.gohighlevel.com/docs/webhook/WebhookIntegrationGuide
- `AppInstall` webhook payload — https://marketplace.gohighlevel.com/docs/webhook/AppInstall
- `AppUninstall` webhook payload — https://marketplace.gohighlevel.com/docs/webhook/AppUninstall
- API v1 end-of-support and migration — https://help.gohighlevel.com/support/solutions/articles/48001060529-highlevel-api
- OAuth consent screen for marketplace apps (install link formats) — https://help.gohighlevel.com/support/solutions/articles/155000005002-api-security-oauth-consent-for-marketplace-apps
- Marketplace app distribution type (target user is permanent) — https://help.gohighlevel.com/support/solutions/articles/155000002141-marketplace-app-distribution-type
- Agency control over which apps sub-accounts may install — https://help.gohighlevel.com/support/solutions/articles/155000001163-managing-marketplace-app-permissions-white-label-agency-control
- Getting started with the developer marketplace — https://help.gohighlevel.com/support/solutions/articles/155000000136-how-to-get-started-with-the-developer-s-marketplace
