Expand description
Unofficial async Rust SDK for the GoHighLevel (HighLevel) CRM API.
Every endpoint has a typed Rust method — 1,203 operations across 45 modules, in API v2 and v3, each with generated request/response types, so you never have to leave the library to read HighLevel’s docs.
use ghl_sdk::{contacts::CreateContact, Ghl};
// Reads GHL_PIT_TOKEN (or GHL_ACCESS_TOKEN) from the environment.
let ghl = Ghl::from_env()?;
let contact = ghl.contacts().create(CreateContact {
location_id: "LOCATION_ID".into(),
email: Some("ada@example.com".into()),
first_name: Some("Ada".into()),
..Default::default()
}).await?;
println!("created {}", contact.id);§What you get
- Auth — Private Integration Tokens, raw OAuth access tokens, or full
OAuth 2.0 with automatic single-flight refresh and a pluggable
TokenStore. Agency→location token exchange viaGhl::as_location. See theauthmodule. - Resilience — 429s retried honoring
Retry-After; 5xx and transport failures retried with exponential backoff + jitter, idempotent methods only, so aPOSTis never silently duplicated. - Rate-limit awareness — live headroom from response headers via
Ghl::rate_status. - Pagination — GoHighLevel’s cursor scheme handled for you and exposed as
futures_util::Streams. - Config by env var or parameter —
Ghl::from_envorGhl::builder; explicit parameters always win. - Secret hygiene — tokens live in
secrecytypes and are redacted from allDebugoutput. - Forward-compatible types — unknown response fields are preserved in an
extramap instead of failing deserialization.
§Generated services — every endpoint, typed
Enable the cargo feature named after an API module and its whole surface appears on the client, with generated parameter and body types:
ghl-sdk = { version = "0.5", features = ["invoices"] }use ghl_sdk::services::invoices::ListInvoicesParams;
// Required query params are constructor arguments; optional ones are setters.
let params = ListInvoicesParams::new(&location_id, "location", "20", "0")
.status("draft");
let page = ghl.invoices().list_invoices(¶ms).await?; // typed response
println!("{:?} invoices", page.total);Every generated method has the same predictable shape:
async fn <name>(&self, <path params…>, params: &XParams, body: &Dto) -> Result<Response>- Path parameters are positional
&strarguments, in URL order. - Query parameters collapse into one
XParamsstruct — required fields arenew()arguments, optional ones are chainable setters. The argument is absent entirely when an endpoint takes no query parameters. - Bodies take the generated DTO from
ghl-models. - Returns the response type the spec names (about 3 in 4 endpoints), else
serde_json::Value.
§API v3
v3 is a parallel, newer surface (627 operations) reached through Ghl::v3,
which sends Version: v3 for you:
let dup = ghl.v3().contacts().get_duplicate_contact(¶ms).await?;It has modules v2 lacks — ad-publishing, social-planner, saas,
chat-widget — and renames three others.
See services for the module list, and the
API reference
for the Rust method behind every endpoint.
§Hand-written helpers
Five modules also carry curated helpers that go beyond a 1:1 endpoint
mapping — they unwrap response envelopes and turn cursor pagination into
futures_util::Streams. They live on the same services, so both styles are
available together:
| Module | Helpers |
|---|---|
contacts | create, get, update, delete, list (streaming) |
opportunities | pipelines, create, get, update, update_status, delete, search (streaming) |
conversations | search, messages, send_message |
calendars | list, free_slots, create_appointment, get_appointment |
locations | get, search |
§Anything not generated
Ghl::request_raw reaches any endpoint — including all of API v3 — with
the same auth, retry, and rate-limit handling:
let dup = ghl.request_raw(
"GET", "/contacts/search/duplicate",
&[("locationId".into(), loc), ("email".into(), email)],
None,
Some("v3"), // v3 endpoints need their own Version header
).await?;§Strict on send, lenient on receive
Request types keep the spec’s required fields non-Option, so a missing
mandatory field is a compile error. Response types make everything optional:
GoHighLevel sometimes omits fields its own spec marks required, and a strict
response type would turn that into an unrecoverable deserialization failure.
§Authentication at a glance
| Situation | Use |
|---|---|
| Internal tool, one sub-account | Auth::private_integration (a pit-… token) |
| You ran OAuth yourself | Auth::access_token (used as-is, never refreshed) |
| Marketplace app | Auth::oauth with a TokenStore — auto-refresh |
| Agency, many sub-accounts | Auth::oauth with UserType::Company, then Ghl::as_location |
GoHighLevel rotates the refresh token on every use, so a TokenStore
implementation must persist durably — MemoryTokenStore loses the session
on restart.
§Webhooks
With the webhooks feature, webhooks::verify checks HighLevel’s
RSA-SHA256 signature and webhooks::WebhookEvent types the envelope its 58
event types share. Verify the raw bytes before parsing. See the webhooks
module.
§Errors
Error distinguishes Error::Api (the API said no, with status and
message), Error::RateLimited (retries exhausted), Error::Auth,
Error::Transport, Error::Decode, and Error::Config. GoHighLevel
returns message as either a string or an array of strings; both normalize
into Error::Api’s message.
§Cargo features
Nothing is on by default. Each API module is its own feature so you compile only the surface you use — one module is a second or two, all 45 is closer to a minute.
| Feature | Effect |
|---|---|
<module> (45 of them, e.g. invoices, payments, products) | That module’s generated services (v2 and v3) plus its DTOs |
full | Every generated service. Convenient, slow to compile |
models | Just the ghl-models re-export, no services |
webhooks | RSA signature verification and typed events (webhooks) |
§Further reading
- Usage guide — auth decision tree, per-module cookbook, pagination, rate limits, multi-location, v2 vs v3, troubleshooting
- Full API reference — all 45 modules: every endpoint, struct, and enum value
ghl-mcp— MCP server built on this SDK, exposing GoHighLevel to AI agents
Not affiliated with HighLevel Inc. “GoHighLevel” and “HighLevel” are trademarks of their respective owners.
Re-exports§
pub use auth::Auth;pub use auth::MemoryTokenStore;pub use auth::OAuthConfig;pub use auth::TokenSet;pub use auth::TokenStore;pub use auth::UserType;pub use ghl_models as models;models
Modules§
- auth
- Authentication: Private Integration Tokens and OAuth 2.0 with automatic refresh.
- calendars
- Calendars — list calendars, find free slots, and book appointments.
- contacts
- Contacts — create, read, update, delete, and stream contact records.
- conversations
- Conversations — search threads, read messages, and send SMS/email.
- locations
- Locations (sub-accounts) — fetch one, or list those a credential can see.
- opportunities
- Opportunities — pipeline deals, their stages, and status transitions.
- services
- Generated typed services — one per GoHighLevel API module, each covering every operation that module exposes.
- webhooks
webhooks - Webhook signature verification and typed events.
Structs§
- Ghl
- Async client for the GoHighLevel API 2.0.
- GhlBuilder
- Builder for
Ghl. - Rate
Status - A snapshot of the most recently observed rate-limit headers.
Enums§
- Error
- All errors returned by this crate.
Constants§
- API_
VERSION - The
Versionheader value used by most API 2.0 modules. - DEFAULT_
BASE_ URL - Production API base URL.
Type Aliases§
- Result
- Crate-wide result alias.