@gnosticdev/highlevel-sdk
Version:
SDK for the HighLevel API
644 lines (635 loc) • 18.2 kB
JavaScript
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
// src/lib/errors.ts
var HighLevelSDKErrorCodes = {
INVALID_AUTH_HEADER: "INVALID_AUTH_HEADER",
INVALID_AUTH_TYPE: "INVALID_AUTH_TYPE",
NO_HANDLER_REGISTERED: "NO_HANDLER_REGISTERED",
INVALID_WEBHOOK_PAYLOAD: "INVALID_WEBHOOK_PAYLOAD",
API_KEY_REQUIRED: "API_KEY_REQUIRED"
};
var ERROR_MESSAGES = {
[HighLevelSDKErrorCodes.INVALID_AUTH_HEADER]: 'Authorization header must start with "Bearer "',
[HighLevelSDKErrorCodes.INVALID_AUTH_TYPE]: "Invalid authentication type provided",
[HighLevelSDKErrorCodes.NO_HANDLER_REGISTERED]: "No handler registered for webhook event: {event}",
[HighLevelSDKErrorCodes.INVALID_WEBHOOK_PAYLOAD]: "Invalid webhook payload",
[HighLevelSDKErrorCodes.API_KEY_REQUIRED]: "apiKey is required"
};
var HighLevelSDKError = class extends Error {
static {
__name(this, "HighLevelSDKError");
}
constructor(message, options) {
const finalMessage = options?.event ? ERROR_MESSAGES[message].replace("{event}", options.event) : ERROR_MESSAGES[message];
super(finalMessage, options);
this.name = "HighLevelSDKError";
this.cause = HighLevelSDKErrorCodes[message];
}
};
// src/v2/client/default.ts
import createClient2 from "openapi-fetch";
// src/v2/client/types.ts
import createClient from "openapi-fetch/src/index.js";
function createClientWithAuth(authHeaders, options) {
if (!authHeaders.Authorization.startsWith("Bearer ")) {
throw new HighLevelSDKError("INVALID_AUTH_HEADER");
}
const config = {
...options,
headers: {
...options?.headers,
...authHeaders
}
};
return createClient({
...config
});
}
__name(createClientWithAuth, "createClientWithAuth");
// src/v2/client/default.ts
var DEFAULT_V2_BASE_URL = "https://services.leadconnectorhq.com";
function createClientMaybeAuth(authHeaders, options) {
if (authHeaders) {
return createClientWithAuth(
authHeaders,
options
);
}
return createClient2(options);
}
__name(createClientMaybeAuth, "createClientMaybeAuth");
var HighLevelClient = class {
constructor(clientConfig, authHeaders) {
this.authHeaders = authHeaders;
this._clientConfig = clientConfig ? {
baseUrl: DEFAULT_V2_BASE_URL,
...clientConfig
} : { baseUrl: DEFAULT_V2_BASE_URL };
if (authHeaders) {
this._clientConfig.headers = {
...this._clientConfig.headers,
...authHeaders
};
}
this.oauth = createClient2(this._clientConfig);
this.businesses = createClientMaybeAuth(
authHeaders,
this._clientConfig
);
this.blogs = createClientMaybeAuth(
authHeaders,
this._clientConfig
);
this.customMenus = createClientMaybeAuth(
authHeaders,
this._clientConfig
);
this.emails = createClientMaybeAuth(
authHeaders,
this._clientConfig
);
this.emailIsv = createClientMaybeAuth(
authHeaders,
this._clientConfig
);
this.calendars = createClientMaybeAuth(
authHeaders,
this._clientConfig
);
this.campaigns = createClientMaybeAuth(
authHeaders,
this._clientConfig
);
this.companies = createClientMaybeAuth(
authHeaders,
this._clientConfig
);
this.courses = createClientMaybeAuth(
authHeaders,
this._clientConfig
);
this.contacts = createClientMaybeAuth(
authHeaders,
this._clientConfig
);
this.conversations = createClientMaybeAuth(
authHeaders,
this._clientConfig
);
this.forms = createClientMaybeAuth(
authHeaders,
this._clientConfig
);
this.funnels = createClientMaybeAuth(
authHeaders,
this._clientConfig
);
this.invoices = createClientMaybeAuth(
authHeaders,
this._clientConfig
);
this.links = createClientMaybeAuth(
authHeaders,
this._clientConfig
);
this.locations = createClientMaybeAuth(
authHeaders,
this._clientConfig
);
this.medias = createClientMaybeAuth(
authHeaders,
this._clientConfig
);
this.opportunities = createClientMaybeAuth(
authHeaders,
this._clientConfig
);
this.locations = createClientMaybeAuth(
authHeaders,
this._clientConfig
);
this.medias = createClientMaybeAuth(
authHeaders,
this._clientConfig
);
this.opportunities = createClientMaybeAuth(
authHeaders,
this._clientConfig
);
this.payments = createClientMaybeAuth(
authHeaders,
this._clientConfig
);
this.products = createClientMaybeAuth(
authHeaders,
this._clientConfig
);
this.saasApi = createClientMaybeAuth(
authHeaders,
this._clientConfig
);
this.snapshots = createClientMaybeAuth(
authHeaders,
this._clientConfig
);
this.socialMediaPosting = createClientMaybeAuth(authHeaders, this._clientConfig);
this.surveys = createClientMaybeAuth(
authHeaders,
this._clientConfig
);
this.users = createClientMaybeAuth(
authHeaders,
this._clientConfig
);
this.workflows = createClientMaybeAuth(
authHeaders,
this._clientConfig
);
}
static {
__name(this, "HighLevelClient");
}
/**
* oauth client implementation.
*/
oauth;
/**
* businesses client implementation.
*/
businesses;
blogs;
customMenus;
emails;
emailIsv;
/**
* invoices client implementation.
*/
invoices;
/**
* opportunities client implementation.
*/
opportunities;
/**
* campaigns client implementation.
*/
campaigns;
/**
* conversations client implementation.
*/
conversations;
/**
* products client implementation.
*/
products;
/**
* courses client implementation.
*/
courses;
/**
* surveys client implementation.
*/
surveys;
/**
* payments client implementation.
*/
payments;
/**
* workflows client implementation.
*/
workflows;
/**
* snapshots client implementation.
*/
snapshots;
/**
* saasApi client implementation.
*/
saasApi;
/**
* users client implementation.
*/
users;
/**
* funnels client implementation.
*/
funnels;
/**
* locations client implementation.
*/
locations;
/**
* links client implementation.
*/
links;
/**
* companies client implementation.
*/
companies;
/**
* contacts client implementation.
*/
contacts;
/**
* forms client implementation.
*/
forms;
/**
* calendars client implementation.
*/
calendars;
/**
* medias client implementation.
*/
medias;
/**
* social media posting client implementation.
*/
socialMediaPosting;
/**
* client configuration
*/
_clientConfig;
};
// src/v2/client/with-integration.ts
var HighLevelIntegrationClient = class extends HighLevelClient {
static {
__name(this, "HighLevelIntegrationClient");
}
/**
* The private token for the integration
*
* @see https://help.leadconnectorhq.com/support/solutions/articles/155000002774-private-integrations-everything-you-need-to-know
*/
privateToken;
/**
* The scopes for the integration.
*
* _NOTE_: in a private integration, we never send off the scopes like we do in Oauth2, but leaving this here for potential future use.
*
* @example
* ```ts
* const scopes = new ScopesBuilder().all().build()
* ```
*/
scopes;
constructor(integrationConfig, clientConfig) {
const authHeaders = {
Authorization: `Bearer ${integrationConfig.privateToken}`,
Version: "2021-07-28"
};
super(clientConfig, authHeaders);
this.privateToken = integrationConfig.privateToken;
this.scopes = integrationConfig.scopes ?? [];
}
};
// src/v2/oauth/impl.ts
import createClient3 from "openapi-fetch";
var OauthClientImpl = class {
static {
__name(this, "OauthClientImpl");
}
_accessToken;
_refreshToken;
/**
* Type of user that is accessing the API.
*
* Only used for generating tokens, similar to `accessType` but uses `Location` (Sub-Account) or `Company` (Agency)
*/
userType;
/**
* Underlying oauth client created by `createClient` method from `openapi-fetch`
*
* Most likely do not need to use this unless special use case.
*/
_client;
// avoid conflict with the `expiresAt` getter
_expiresAt;
scopes;
config;
baseUrl;
baseOauthUrl;
// avoid conflict with the `tokenData` property
tokenData;
storeTokenFn;
/**
* creates a new oauth client for use with the HighLevel API
* @constructor
* @param config - configuration for your app
*/
constructor(config) {
this.config = config;
this.baseUrl = config.baseUrl ?? DEFAULT_V2_BASE_URL;
this.baseOauthUrl = config.baseAuthUrl ?? DEFAULT_BASE_AUTH_URL;
this.scopes = Array.isArray(config.scopes) ? config.scopes.join(" ") : config.scopes;
if (this.scopes.length === 0) {
console.warn(
"No scopes provided! Pass the scopes set in your app to the scopes param"
);
}
this.userType = config.accessType === "Sub-Account" ? "Location" : "Company";
this.storeTokenFn = config.storageFunction ?? (() => {
if (!this.tokenData) {
throw new Error("No token data to store");
}
return Promise.resolve(this.tokenData);
});
this._client = createClient3({
baseUrl: this.baseUrl
});
}
/**
* The time (in seconds) when the access token expires.
*/
get expiresAt() {
return this._expiresAt;
}
/**
* Update the stored token data with the provided token data.
* @param updatedTokenData - The token data to update.
*/
updateTokenData(updatedTokenData) {
this.tokenData = { ...this.tokenData, ...updatedTokenData };
this.storeTokenFn(this.tokenData);
}
/**
* generates the authorization url for your app using the baseAuthUrl, clientId, redirectUri, and scopes.
* @see https://highlevel.stoplight.io/docs/integrations/a04191c0fabf9-authorization
* @example
* ```ts
https://marketplace.leadconnectorhq.com/oauth/chooselocation?response_type=code&redirect_uri=https://myapp.com/oauth/callback/gohighlevel&client_id=CLIENT_ID&scope=conversations/message.readonly conversations/message.write
* ```
*/
getAuthorizationUrl() {
const url = new URL(this.baseOauthUrl);
const requiredParams = {
client_id: this.config.clientId,
redirect_uri: this.config.redirectUri,
scope: this.scopes,
response_type: "code"
};
const searchParams = new URLSearchParams(requiredParams);
url.search = searchParams.toString();
return url.toString();
}
/**
* Stores everything from the Token response including the accessToken, refreshToken, locationID, and adds the expiresAt time (in ms).
* **NOTE**: You can add a `storageFunction` to the config to store the token data in your database or cache.
* @param tokenData - the token response from the server
* @returns the token data with the `expiresAt` time added
*/
async storeTokenData(tokenData) {
this._accessToken = tokenData.access_token;
this._refreshToken = tokenData.refresh_token;
const expiresAt = Date.now() + tokenData.expires_in * 1e3;
this.tokenData = { ...tokenData, expiresAt };
return this.storeTokenFn(this.tokenData);
}
/**
* Check if the token is expired or if we need to refresh it
* @returns true if the token is expired or if we need to refresh it
*/
isTokenExpired() {
if (!this.tokenData?.expiresAt || !this._refreshToken) return true;
return this.tokenData.expiresAt <= Date.now() + 5 * 60 * 1e3;
}
/**
* Returns a valid access token by either refreshing the token or exchanging the auth code for a new token.
*
* Also stores the token via the `storeTokenData` if provided.
* @param authCode - The authorization code received from the OAuth provider.
* @throws {Error} - If no token response is received or if no auth code or refresh token is provided.
* @returns The access token.
*/
async getAccessToken(authCode) {
if (this._accessToken && !this.isTokenExpired()) {
return this._accessToken;
}
if (authCode) {
const tokenResponse2 = await this.exchangeToken(authCode);
if (!tokenResponse2) throw new Error("Error in token exchange");
const storedToken2 = await this.storeTokenData(tokenResponse2);
return storedToken2.access_token;
}
if (!this._refreshToken) {
return null;
}
const tokenResponse = await this.refreshAccessToken();
const storedToken = await this.storeTokenData(tokenResponse);
return storedToken.access_token;
}
/**
* Generates a new access token using the provided authorization code. \
* **NOTE**: This method does not save the token data to the client.
* To store the token:
* - `storeTokenData` - saves the token data.
* - `getAccessToken` - fetches and stores the token data.
* @param authCode - Parameters required to generate a new token.
* @returns The token response from the server.
*/
async exchangeToken(authCode) {
const tokenParams = {
client_id: this.config.clientId,
client_secret: this.config.clientSecret,
grant_type: "authorization_code",
user_type: this.userType,
code: authCode
};
const { data, error } = await this.#fetchAccessToken(tokenParams);
if (error) {
console.error("Error: exchanging token", error);
throw new Error(error.message?.toString() ?? String(error));
}
return data;
}
/**
* Refreshes the current access token.
* **NOTE**: This method does not save the token data to the client.
* To store the token:
* - `storeTokenData` - saves the token data.
* - `getAccessToken` - fetches and stores the token data.
* @returns The token response from the server.
*/
async refreshAccessToken() {
if (!this._refreshToken) {
throw new Error("No refresh token available.");
}
const tokenParams = {
client_id: this.config.clientId,
client_secret: this.config.clientSecret,
refresh_token: this._refreshToken,
grant_type: "refresh_token",
user_type: this.userType
};
const { data, error } = await this.#fetchAccessToken(tokenParams);
if (error) {
console.error("Error: refreshing token", error);
throw new Error(error.message?.toString() ?? String(error));
}
return data;
}
/**
* Main method for getting getting a token from HighLevel v2 API.
*
* Sends a `POST` request to the `/oauth/token` endpoint.
*
* @param tokenParams - Auth code params or refresh token params
* @private
*/
async #fetchAccessToken(tokenParams) {
return this._client.POST("/oauth/token", {
body: tokenParams,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
Accept: "application/json"
},
bodySerializer(_body) {
return new URLSearchParams(_body).toString();
}
});
}
/**
* generate a location AccessToken from Agency AccessToken
* @param companyId - your agency id
* @param locationId - The locationId is the locationId of the location you want to get a token for
*/
async generateLocationToken({ companyId, locationId }) {
const { data, error, response } = await this._client.POST(
"/oauth/locationToken",
{
body: {
companyId,
locationId
},
params: {
header: {
Version: "2021-07-28"
}
},
bodySerializer(_body) {
return new URLSearchParams(_body).toString();
},
headers: {
"Content-Type": "application/x-www-form-urlencoded",
Accept: "application/json"
}
}
);
if (error) {
console.error(await response.text());
throw new Error(error.message?.toString() ?? String(error));
}
return data;
}
/**
* Get all locations under your agency that have installed your app
* @param query - search for installed locations using any of these properties
* @param appId - the appId of your app
* @param companyId - the companyId of your agency
*/
async getInstalledLocations(query) {
const { data, error } = await this._client.GET(
"/oauth/installedLocations",
{
params: {
query: {
...query,
appId: query.appId,
companyId: query.companyId
},
header: {
Version: "2021-07-28"
}
}
}
);
if (error) {
console.error("Error: installed locations", error);
throw new Error(error.message?.toString());
}
return data;
}
};
// src/v2/client/with-oauth.ts
var DEFAULT_BASE_AUTH_URL = "https://marketplace.leadconnectorhq.com/oauth/chooselocation";
var HighLevelClientWithOAuth = class extends HighLevelClient {
static {
__name(this, "HighLevelClientWithOAuth");
}
oauth;
constructor(oauthConfig, clientConfig) {
super(clientConfig);
const baseUrl = clientConfig?.baseUrl ?? oauthConfig.baseUrl ?? DEFAULT_V2_BASE_URL;
const _oauthConfig = {
...oauthConfig,
baseUrl,
baseAuthUrl: oauthConfig?.baseAuthUrl ?? DEFAULT_BASE_AUTH_URL,
scopes: oauthConfig.scopes ?? []
};
this.oauth = new OauthClientImpl(_oauthConfig);
}
};
// src/v2/index.ts
function createHighLevelClient2(clientConfig, authType, authConfig) {
if (!authType || !authConfig) {
return new HighLevelClient(
clientConfig
);
}
if (authType === "oauth" && "clientId" in authConfig) {
return new HighLevelClientWithOAuth(
authConfig,
clientConfig
);
}
if (authType === "integration" && "privateToken" in authConfig) {
return new HighLevelIntegrationClient(
authConfig,
clientConfig
);
}
throw new HighLevelSDKError("INVALID_AUTH_TYPE");
}
__name(createHighLevelClient2, "createHighLevelClient");
export {
createHighLevelClient2 as createHighLevelClient
};