### Complete Server Setup (TypeScript) Source: https://context7.com/gohighlevel/ghl-marketplace-app-template/llms.txt Sets up a full Express server with middleware, OAuth routes, API endpoint examples, webhook handling, SSO decryption, and frontend serving. It utilizes `express`, `dotenv`, `body-parser`, and a custom `GHL` class. Dependencies include `express`, `dotenv`, and `body-parser`. ```typescript import express from "express"; import dotenv from "dotenv"; import { json } from "body-parser"; import { GHL } from "./ghl"; const path = __dirname + "/ui/dist/"; dotenv.config(); const app = express(); const ghl = new GHL(); // Middleware app.use(json({ type: 'application/json' })); app.use(express.static(path)); // OAuth routes app.get("/authorize-handler", async (req, res) => { const { code } = req.query; await ghl.authorizationHandler(code); res.redirect("https://app.gohighlevel.com/"); }); // API routes app.get("/example-api-call", async (req, res) => { if (ghl.checkInstallationExists(req.query.companyId)) { const response = await ghl .requests(req.query.companyId) .get(`/users/search?companyId=${req.query.companyId}`, { headers: { Version: "2021-07-28" } }); return res.json(response.data); } res.status(404).send("Installation not found"); }); // Webhook routes app.post("/example-webhook-handler", async (req, res) => { console.log("Webhook:", req.body); res.status(200).send("OK"); }); // SSO routes app.post("/decrypt-sso", async (req, res) => { try { const data = ghl.decryptSSOData(req.body.key); res.json(data); } catch (error) { res.status(400).send("Invalid Key"); } }); // Frontend routes app.get("/", (req, res) => { res.sendFile(path + "index.html"); }); const port = process.env.PORT || 3000; app.listen(port, () => { console.log(`GHL app listening on port ${port}`); }); ``` -------------------------------- ### Verify GoHighLevel App Installation (TypeScript) Source: https://context7.com/gohighlevel/ghl-marketplace-app-template/llms.txt Checks if the application is installed for a specific company or location before initiating API calls. The `validateInstallation` function leverages the `ghl.checkInstallationExists` method. If not installed, it throws an error, preventing unauthorized API requests. This is demonstrated within an example route handler. ```typescript // Check if app is installed before making requests function validateInstallation(resourceId) { const isInstalled = ghl.checkInstallationExists(resourceId); if (!isInstalled) { throw new Error(`App not installed for resource: ${resourceId}`); } return isInstalled; } // Usage in route handler app.get("/api/custom-endpoint", async (req, res) => { const { companyId } = req.query; try { validateInstallation(companyId); // Proceed with API calls const data = await ghl.requests(companyId).get("/custom/endpoint"); res.json(data.data); } catch (error) { res.status(404).json({ error: error.message }); } }); ``` -------------------------------- ### Environment Configuration (.env and Node.js) Source: https://context7.com/gohighlevel/ghl-marketplace-app-template/llms.txt Defines required environment variables for connecting to the GoHighLevel API and managing the OAuth flow. It includes example `.env` variables and Node.js code to load and access these variables using the `dotenv` package. Ensure these variables are set in your deployment environment. ```bash # .env file configuration GHL_APP_CLIENT_ID=your_client_id_from_marketplace GHL_APP_CLIENT_SECRET=your_client_secret_from_marketplace GHL_APP_SSO_KEY=your_sso_key_from_marketplace GHL_API_DOMAIN=https://services.leadconnectorhq.com PORT=3000 ``` ```javascript # Usage in application import dotenv from "dotenv"; dotenv.config(); const config = { clientId: process.env.GHL_APP_CLIENT_ID, clientSecret: process.env.GHL_APP_CLIENT_SECRET, ssoKey: process.env.GHL_APP_SSO_KEY, apiDomain: process.env.GHL_API_DOMAIN, port: process.env.PORT || 3000 }; ``` -------------------------------- ### OAuth Authorization Handler Source: https://context7.com/gohighlevel/ghl-marketplace-app-template/llms.txt Handles the OAuth authorization callback from GoHighLevel. It exchanges the authorization code for access and refresh tokens and stores the installation details. ```APIDOC ## POST /authorize-handler ### Description Processes the OAuth authorization callback from GoHighLevel, exchanges the authorization code for access and refresh tokens, and stores the installation details for future API requests. ### Method GET ### Endpoint /authorize-handler ### Parameters #### Query Parameters - **code** (string) - Required - The authorization code received from GoHighLevel. ### Request Example ``` GET /authorize-handler?code=AUTHORIZATION_CODE ``` ### Response #### Success Response (302) Redirects the user back to the GoHighLevel application upon successful authorization. #### Error Response (400) - **message** (string) - "Authorization code is required" #### Error Response (500) - **message** (string) - "Authorization failed" ``` -------------------------------- ### Model Class - Token Management (TypeScript) Source: https://context7.com/gohighlevel/ghl-marketplace-app-template/llms.txt Manages the storage and retrieval of access tokens, refresh tokens, and installation details for multiple companies and locations. It uses a Model class to abstract data operations. Dependencies include a local 'model' instance. ```typescript import { Model, InstallationDetails, AppUserType } from "./model"; const model = new Model(); // Saving installation details after OAuth async function handleInstallation(oauthResponse) { const installationDetails = { access_token: "eyJhbGciOiJIUzI1...", token_type: "Bearer", expires_in: 86400, refresh_token: "refresh_token_value", scope: "contacts.readonly users.readonly", userType: AppUserType.Company, companyId: "company123" }; await model.saveInstallationInfo(installationDetails); } // Retrieving tokens function getTokens(resourceId) { const accessToken = model.getAccessToken(resourceId); const refreshToken = model.getRefreshToken(resourceId); return { accessToken, refreshToken }; } // Updating tokens after refresh function updateTokens(resourceId, newAccessToken, newRefreshToken) { model.setAccessToken(resourceId, newAccessToken); model.setRefreshToken(resourceId, newRefreshToken); } ``` -------------------------------- ### Make Company-Level API Requests with GoHighLevel SDK Source: https://context7.com/gohighlevel/ghl-marketplace-app-template/llms.txt This code demonstrates making authenticated API calls to GoHighLevel at the company level using the GHL class. It automatically handles token refresh and authorization headers. The snippet requires a 'companyId' from query parameters and utilizes the 'ghl.requests(companyId).get()' method to interact with the GoHighLevel API. It checks for existing installations and returns API data or an error response. ```typescript // GET /example-api-call?companyId=COMPANY_ID app.get("/example-api-call", async (req, res) => { const { companyId } = req.query; // Check if app is installed for this company if (!ghl.checkInstallationExists(companyId)) { return res.status(404).send("Installation for this company does not exist"); } try { // Make authenticated request to GHL API const response = await ghl .requests(companyId) .get(`/users/search?companyId=${companyId}`, { headers: { Version: "2021-07-28", }, }); // Response example: // { // "users": [ // { "id": "user123", "email": "user@example.com", "name": "John Doe" } // ] // } return res.json(response.data); } catch (error) { console.error("API call failed:", error); res.status(500).send("Failed to fetch users"); } }); ``` -------------------------------- ### Handle OAuth Authorization Callback with Express.js Source: https://context7.com/gohighlevel/ghl-marketplace-app-template/llms.txt This snippet demonstrates how to process the OAuth authorization callback from GoHighLevel using an Express.js server. It exchanges the authorization code for access and refresh tokens and stores installation details. Dependencies include 'express' and a custom 'GHL' class for handling GoHighLevel interactions. The input is the authorization 'code' from the query parameters, and the output is a redirect to the GoHighLevel app or an error response. ```typescript import express from "express"; import { GHL } from "./ghl"; const app = express(); const ghl = new GHL(); // Configure in your GHL app settings as: http://localhost:3000/authorize-handler app.get("/authorize-handler", async (req, res) => { try { const { code } = req.query; if (!code) { return res.status(400).send("Authorization code is required"); } // Exchanges code for tokens and saves installation await ghl.authorizationHandler(code); // Redirect user back to GHL after successful authorization res.redirect("https://app.gohighlevel.com/"); } catch (error) { console.error("Authorization failed:", error); res.status(500).send("Authorization failed"); } }); ``` -------------------------------- ### GHL Class: Authenticated Request Wrapper (TypeScript) Source: https://context7.com/gohighlevel/ghl-marketplace-app-template/llms.txt Provides a wrapper class 'GHL' to create an Axios instance for making authenticated API calls to GoHighLevel. It automatically injects authorization headers and handles token refresh logic, simplifying API interactions. The `requests` method takes a company ID to configure the API client. ```typescript import { GHL } from "./ghl"; const ghl = new GHL(); // Example: Making custom API calls async function fetchCustomData(companyId) { // Create authenticated request instance const apiClient = ghl.requests(companyId); try { // GET request example const opportunities = await apiClient.get("/opportunities/search", { params: { pipelineId: "pipeline123" }, headers: { Version: "2021-07-28" } }); // POST request example const newContact = await apiClient.post("/contacts/", { firstName: "Jane", lastName: "Smith", email: "jane@example.com", locationId: "location123" }, { headers: { Version: "2021-07-28" } }); // PUT request example const updatedContact = await apiClient.put(`/contacts/${newContact.data.contact.id}`, { customField: "custom_value" }, { headers: { Version: "2021-07-28" } }); return { opportunities: opportunities.data, newContact: newContact.data }; } catch (error) { if (error.response?.status === 401) { // Automatically handled by interceptor - token refreshed console.log("Token refreshed automatically"); } throw error; } } ``` -------------------------------- ### Company-Level API Request Source: https://context7.com/gohighlevel/ghl-marketplace-app-template/llms.txt Makes authenticated API calls to GoHighLevel at the company level. It automatically handles token refresh and authorization headers using the GHL requests wrapper. ```APIDOC ## GET /example-api-call ### Description Makes authenticated API calls to GoHighLevel at the company level, automatically handling token refresh and authorization headers through the GHL requests wrapper. ### Method GET ### Endpoint /example-api-call ### Parameters #### Query Parameters - **companyId** (string) - Required - The ID of the company for which the API call is being made. ### Request Example ``` GET /example-api-call?companyId=COMPANY_ID ``` ### Response #### Success Response (200) - **users** (array) - A list of user objects for the specified company. - **id** (string) - The user's unique identifier. - **email** (string) - The user's email address. - **name** (string) - The user's name. #### Error Response (404) - **message** (string) - "Installation for this company does not exist" #### Error Response (500) - **message** (string) - "Failed to fetch users" ``` -------------------------------- ### Vue 3 Frontend SSO Integration (JavaScript) Source: https://context7.com/gohighlevel/ghl-marketplace-app-template/llms.txt Provides a JavaScript client for Vue 3 applications to retrieve and decrypt user session data when embedded within GoHighLevel iframes. It relies on a global `ghl` object initialized with the GHL class. Inputs include requests to the parent window for user data, and outputs are the decrypted user data or errors. ```javascript // src/ui/src/ghl/index.js import { GHL } from "./ghl"; // Initialize in Vue app const ghl = new GHL(); window.ghl = ghl; // Usage in Vue component export default { name: 'UserProfile', data() { return { userData: null, loading: false, error: null }; }, async mounted() { await this.loadUserData(); }, methods: { async loadUserData() { this.loading = true; try { // Requests encrypted data from parent window // Sends to backend for decryption const userData = await window.ghl.getUserData(); // userData example: // { // "userId": "user123", // "companyId": "company456", // "email": "user@example.com", // "name": "John Doe", // "role": "admin" // } this.userData = userData; console.log("Logged in user:", userData.email); } catch (error) { console.error("Failed to load user data:", error); this.error = "Unable to authenticate user"; } finally { this.loading = false; } } } }; ``` -------------------------------- ### Location-Level API Request with Token Exchange Source: https://context7.com/gohighlevel/ghl-marketplace-app-template/llms.txt Handles location-specific API calls. It automatically obtains location tokens from company tokens when needed for multi-location integrations. ```APIDOC ## GET /example-api-call-location ### Description Handles location-specific API calls, automatically obtaining location tokens from company tokens when needed for multi-location integrations. ### Method GET ### Endpoint /example-api-call-location ### Parameters #### Query Parameters - **companyId** (string) - Required - The ID of the company. - **locationId** (string) - Required - The ID of the location for which the API call is being made. ### Request Example ``` GET /example-api-call-location?companyId=COMPANY_ID&locationId=LOCATION_ID ``` ### Response #### Success Response (200) - **contacts** (array) - A list of contact objects for the specified location. - **id** (string) - The contact's unique identifier. - **email** (string) - The contact's email address. - **firstName** (string) - The contact's first name. - **locationId** (string) - The ID of the location associated with the contact. #### Error Response (400) - **error** (string) - A message describing the error that occurred during the API call. ``` -------------------------------- ### Handle GoHighLevel Webhook Events (TypeScript) Source: https://context7.com/gohighlevel/ghl-marketplace-app-template/llms.txt Processes incoming webhook events from GoHighLevel. This handler can manage various event types like contact updates, appointment changes, or form submissions. It requires the 'body-parser' middleware for JSON payload parsing and acknowledges receipt by sending a 200 OK status. ```typescript import { json } from "body-parser"; app.use(json({ type: 'application/json' })); // POST /example-webhook-handler // Configure in GHL app settings under Webhook URLs app.post("/example-webhook-handler", async (req, res) => { const webhookPayload = req.body; // Example payload structure: // { // "type": "Contact.Create", // "locationId": "LOCATION_ID", // "contact": { // "id": "contact123", // "email": "newcontact@example.com", // "firstName": "John" // } // } console.log("Webhook received:", webhookPayload); // Process webhook based on event type switch (webhookPayload.type) { case "Contact.Create": // Handle new contact creation console.log("New contact created:", webhookPayload.contact); break; case "Appointment.Create": // Handle appointment booking console.log("New appointment:", webhookPayload.appointment); break; default: console.log("Unhandled event type:", webhookPayload.type); } // Acknowledge webhook receipt res.status(200).send("OK"); }); ``` -------------------------------- ### Perform Location-Level API Calls with Token Exchange in GoHighLevel Source: https://context7.com/gohighlevel/ghl-marketplace-app-template/llms.txt This snippet illustrates how to make location-specific API calls to GoHighLevel, including exchanging company tokens for location tokens when necessary. It requires 'companyId' and 'locationId' from query parameters. The 'ghl.getLocationTokenFromCompanyToken()' function is used for token exchange, assuming appropriate scopes and distribution types are configured. The response contains location-specific data or an error message. ```typescript // GET /example-api-call-location?companyId=COMPANY_ID&locationId=LOCATION_ID app.get("/example-api-call-location", async (req, res) => { const { companyId, locationId } = req.query; try { // Check if location token already exists if (!ghl.checkInstallationExists(locationId)) { // Exchange company token for location token // Requires OAuth read-write scopes and distribution type: Company & Location await ghl.getLocationTokenFromCompanyToken(companyId, locationId); } // Make location-specific API call const response = await ghl .requests(locationId) .get(`/contacts/?locationId=${locationId}`, { headers: { Version: "2021-07-28", }, }); // Response example: // { // "contacts": [ // { // "id": "contact123", // "email": "contact@example.com", // "firstName": "Jane", // "locationId": "LOCATION_ID" // } // ] // } return res.json(response.data); } catch (error) { console.error("Location API call failed:", error); res.status(400).json({ error: error.message }); } }); ``` -------------------------------- ### Decrypt GoHighLevel SSO Data (TypeScript) Source: https://context7.com/gohighlevel/ghl-marketplace-app-template/llms.txt Decrypts encrypted SSO session data received from GoHighLevel. This is crucial for securing access to user session information when a custom page is embedded within an iframe. It relies on a GHL_APP_SSO_KEY from environment variables for decryption and requires the 'dotenv' package for configuration. ```typescript import dotenv from "dotenv"; dotenv.config(); // POST /decrypt-sso app.post("/decrypt-sso", async (req, res) => { const { key } = req.body; if (!key) { return res.status(400).send("Please send valid key"); } try { // Decrypts using GHL_APP_SSO_KEY from environment const decryptedData = ghl.decryptSSOData(key); // Response example: // { // "userId": "user123", // "companyId": "company456", // "locationId": "location789", // "email": "user@example.com", // "name": "John Doe", // "role": "admin" // } res.json(decryptedData); } catch (error) { console.error("SSO decryption failed:", error); res.status(400).send("Invalid Key"); } }); ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.