Official PHP SDK for the GoHighLevel API. Build powerful integrations with the all-in-one marketing and CRM platform for agencies.
- PHP >= 7.4
- ext-json
- ext-curl
- composer
The SDK ships with an in-memory SessionStorage implementation that works out of the box. If you want persistent MongoDB-backed session storage via HighLevel\Storage\MongoDBSessionStorage, install the MongoDB packages separately:
# PHP 7.4 / 8.0
composer require mongodb/mongodb:^1.17
# PHP 8.1+
composer require mongodb/mongodb:^2.0You will also need the PECL mongodb extension (pecl install mongodb).
Install the SDK using Composer:
composer require gohighlevel/api-clientPlease take a look at Sample Apps
<?phprequire_once'vendor/autoload.php';
useHighLevel\HighLevel;
useHighLevel\HighLevelConfig;
useHighLevel\Services\Contacts\Models\SearchBodyV2DTO;
// Initialize the client$config = newHighLevelConfig([
'privateIntegrationToken' => 'your-private-token-here'
]);
or with $config = newHighLevelConfig([
'clientId' => 'your-client-id', // $_ENV['CLIENT_ID']'clientSecret' => 'your-client-secret'// $_ENV['CLIENT_SECRET']
]);
$ghl = newHighLevel($config);
// Get contacts$requestBody = newSearchBodyV2DTO([
'locationId' => 'zBG0T99IsBgOoXUrcROH',
'pageLimit' => 1
]);
$contactsResponse = $ghl->contacts->searchContactsAdvanced($requestBody);
error_log('Fetched contacts: ' . json_encode($contactsResponse, JSON_PRETTY_PRINT));<?phpuseHighLevel\HighLevel;
useHighLevel\HighLevelConfig;
useHighLevel\Storage\SessionData;
// Initialize with OAuth credentials$config = newHighLevelConfig([
'clientId' => 'your-client-id',
'clientSecret' => 'your-client-secret'
]);
$ghl = newHighLevel($config);
// Step 1: Redirect user to authorization URL$authUrl = $ghl->oauth->getAuthorizationUrl(
'your-client-id',
'https://your-app.com/callback',
'contacts.readonly contacts.write'// add all scopes here(one space seperated)
);
header('Location: ' . $authUrl);
exit;
// Step 2: Exchange authorization code for access token (in callback)$tokenData = $ghl->oauth->getAccessToken([
'code' => $_GET['code'],
'client_id' => 'your-client-id',
'client_secret' => 'your-client-secret',
'grant_type' => 'authorization_code'
]);
// Step 3: Store the session$locationId = $tokenData->location_id
$ghl->getSessionStorage()->setSession($locationId, newSessionData($tokenData));$config = newHighLevelConfig([
// Authentication (choose one)'privateIntegrationToken' => 'token', // For private integrations'clientId' => 'id', // For OAuth'clientSecret' => 'secret', // For OAuth'agencyAccessToken' => 'token', // Temporary agency token'locationAccessToken' => 'token', // Temporary location token// Optional settings'apiVersion' => '2021-07-28', // API version'sessionStorage' => $customStorage, // Custom storage implementation'logLevel' => 'warn'// debug|info|warn|error|silent
]);Implement your own storage (database, Redis, etc.):
<?phpuseHighLevel\Storage\SessionStorage;
class DatabaseSessionStorage extends SessionStorage
{
private$pdo;
publicfunction__construct(\PDO$pdo, $logger = null)
{
parent::__construct($logger);
$this->pdo = $pdo;
}
publicfunctionsetSession(string$resourceId, array$sessionData): void
{
$stmt = $this->pdo->prepare(
'INSERT INTO ghl_sessions (resource_id, data, updated_at) VALUES (?, ?, NOW()) ON DUPLICATE KEY UPDATE data = ?, updated_at = NOW()'
);
$json = json_encode($sessionData);
$stmt->execute([$resourceId, $json, $json]);
}
publicfunctiongetSession(string$resourceId): ?array
{
$stmt = $this->pdo->prepare(
'SELECT data FROM ghl_sessions WHERE resource_id = ?'
);
$stmt->execute([$resourceId]);
$result = $stmt->fetch(\PDO::FETCH_ASSOC);
return$result ? json_decode($result['data'], true) : null;
}
// Implement other required methods...
}
// Use custom storage$pdo = new \PDO('mysql:host=localhost;dbname=myapp', 'user', 'password');
$config = newHighLevelConfig([
'clientId' => 'your-client-id',
'clientSecret' => 'your-client-secret',
'sessionStorage' => newDatabaseSessionStorage($pdo)
]);useHighLevel\GHLError;
try {
$contact = $ghl->contacts->getContact([
'contactId' => 'invalid-id',
'locationId' => 'location-123'
]);
} catch (GHLError$e) {
echo"Error: " . $e->getMessage() . "\n";
echo"Status Code: " . $e->getStatusCode() . "\n";
echo"Response: " . json_encode($e->getResponse()) . "\n";
}processWebhook verifies the incoming signature, then for INSTALL events it auto-generates and stores a location access token, and for UNINSTALL events it removes the stored token. Two signature schemes are supported:
| Header | Scheme | Public key env var |
|---|---|---|
x-ghl-signature | Ed25519 (preferred) | WEBHOOK_SIGNATURE_PUBLIC_KEY |
x-wh-signature | RSA-SHA256 (legacy) | WEBHOOK_PUBLIC_KEY |
If both signatures are received, Ed25519 takes precedence.
Set the following environment variables in your app — the SDK reads them internally, so you never pass keys or the client id as method arguments:
WEBHOOK_SIGNATURE_PUBLIC_KEY=...# Ed25519 public keyWEBHOOK_PUBLIC_KEY=...# RSA public key (legacy)CLIENT_ID=...# OAuth client id; enables appId validationThe SDK checks $_ENV, $_SERVER, and getenv() in that order, so it works under PHP-FPM, CLI, and frameworks like Laravel/Slim that hydrate $_ENV via phpdotenv.
Pass only the raw payload and the signature header values from the incoming HTTP request:
$payload = $request->getBody()->getContents(); // raw request body as string$whSig = $request->getHeaderLine('x-wh-signature') ?: null; // RSA (legacy)$ghlSig = $request->getHeaderLine('x-ghl-signature') ?: null; // Ed25519$result = $ghl->getWebhookManager()->processWebhook($payload, $whSig, $ghlSig);processWebhook returns an associative array:
[
'processed' => true, // bool — false if signature invalid or appId mismatch'type' => 'INSTALL', // string — webhook event type'signatureType' => 'ed25519', // 'ed25519' | 'rsa' | null (null = skipped)'signatureValid' => true, // bool'skippedSignatureVerification' => false, // bool — true if no key/signature was supplied
]On failure it returns ['processed' => false, 'reason' => 'invalid_signature' | 'app_id_mismatch', ...].
You can also verify a signature without running the full webhook pipeline. These helpers do take the public key as an argument, so you can verify against any key you already have in memory:
// RSA-SHA256 (x-wh-signature)$ghl->getWebhookManager()->verifySignature($payload, $signature, $rsaPublicKeyPem);
// Ed25519 (x-ghl-signature) — uses libsodium under the hood$ghl->getWebhookManager()->verifyEd25519Signature($payload, $ghlSignature, $ed25519PublicKeyPem);verifyEd25519Signature accepts the public key as PEM (SPKI), raw 32 bytes, 64-character hex, or base64. Requires the sodium extension, which is bundled with PHP 7.2+.
This SDK is open-sourced software licensed under the MIT license.
See CHANGELOG.md for version history.