Docs
Authoring Scripts
JavaScript + CSS bodies, location assignments, execution order, and version history.
JavaScript and CSS bodies
Each script carries a jsBody, a cssBody, or both, depending on the Type setting in the editor. Bodies are plain source — no <script> or<style> wrapper. The loader injects them into the customer page at runtime.
// Example JS body
(function () {
const banner = document.createElement("div");
banner.textContent = "Welcome 👋";
banner.style.cssText = "position:fixed;top:0;left:0;right:0;padding:1rem;background:#818cf8;color:white;text-align:center;z-index:9999";
document.body.appendChild(banner);
})();(function(){…})();) so your variables don't pollute the global scope. The loader injects all active scripts into the same page — you don't want two scripts both declaring let x at the top level.Execution order (priority)
If you have multiple scripts that need to run in a specific sequence (e.g. a utilities library before a script that depends on it), use the Priority field. Lower numbers run first. The default is 100. Typical patterns:
50— shared utility scripts (libraries, polyfills)100— most scripts200— scripts that depend on others and should run last
Location assignment modes
The Locations tab on each script controls which GHL sub-accounts the script runs on. Three modes:
- All— runs on every synced sub-account, plus pages where the loader can't detect a location (the homepage, some settings pages).
- Selected — runs only on the sub-accounts you check. Most common for client-specific scripts.
- All except — runs everywhere EXCEPT the sub-accounts you check. Useful when a script should run agency-wide but a single client needs to opt out.
locationId field to your config and gate the script with if (window.location.href.includes(id)) …. The Locations tab is the single source of truth — the loader only ships your script to allowlisted sub-accounts, so any code that reaches it is already authorized to run. A self-gate adds a second, weaker check that silently no-ops on URLs it doesn't recognize and limits the script to one sub-account per install. If you need per-location behavior (different logos per client), create one script per location or read window.location.hreffor branching only — don't make the installer paste their own location ID.Publish / draft / archive
Every script has one of three statuses, visible as a dot next to the title:
- Live — the script is in the loader bundle and running on assigned locations.
- Draft — saved but not serving. Useful while you iterate on code without exposing half-finished work to your customers.
- Archived — removed from the bundle entirely. The row stays in your dashboard (toggle the filter to see archived scripts) so you can restore or delete later.
The Publish / Unpublishbutton in the script header toggles between Draft and Live. It's a separate concept from publishing to the Marketplace— that's covered in Publishing.
Version history
Every save creates a new version. The History tab lists them with diffs and a one-click Restore button that rolls the body back to a previous revision. Free keeps the last 5 versions; Starter and above keep unlimited history.
Kill switch
If a script is breaking a customer page, don't panic-delete it. Flip the workspace kill switch in Settings → Workspace. The loader immediately returns an empty bundle for every page — zero scripts inject workspace-wide. Flip it back off when you've fixed the culprit.
Scheduled publish / unpublish
Click Schedule in the script header to set a future time when the script should auto-flip to Live or Draft. You can set either, both, or neither — leave a field blank to skip it.
- Activate at — status flips to Live at the given time. Typical use: publish a campaign script the morning of launch day.
- Deactivate at — status flips to Draftat the given time. Typical use: end-date for a promo banner so you don't have to remember to turn it off.
A background job runs every 5 minutes, so a scheduled change lands within that window plus the loader's ~30-second CDN revalidation. Setting a schedule doesn't touch the current status — if you schedule an activation for tomorrow but the script is already Draft today, it stays Draft until tomorrow.
Script error monitoring
The Healthtab on each script shows runtime errors captured from customer pages. When a script throws, the loader's built-in try/catch catches it, logs to the customer's console, and beacons the error to your workspace — the page keeps working and you see the failure in your dashboard.
Each error row has:
- The error message + stack trace
- The GHL location ID where it fired
- The page URL the customer was on
- The browser user agent
The KPI row at the top shows counts for the last 24 hours, 7 days, and 30 days — quick signal for “is this script healthy?” before you dig into individual rows. The tab auto-polls every 30 seconds while you're looking at it and stops when the browser tab loses focus.
Bulk location actions
In Settings → Integrations → Connected Locations, each row has a checkbox plus a select-all in the header. When any are selected, an action bar appears with:
- Enable / Disable — flip the
enabledflag on every selected row. Enable respects the plan cap atomically: if enabling all selected would push you over the location limit, the whole operation is rejected with a 402 and a clear message. No partial state. - Set config…— type a key and value, click Apply, and the key lands in every selected location's
window.veloxscriptConfig. Tick “Remove this key” to delete instead of set. Existing keys with the same name get overwritten.
One audit row per affected location per action, one loader-cache invalidation for the whole workspace (so all customer pages pick up the change within the standard CDN TTL — no n+1 revalidations).
