This is the abridged developer documentation for GymMark
# GymMark Docs
> Set up and run GymMark on your gym's WordPress site.
[Getting started](/help/getting-started/)What GymMark adds to your site and the first settings to check. [Booking forms](/help/forms/forms-mode/)Turn forms on safely, test them privately, then go live. [Connect HighLevel](/help/integrations/highlevel/)Send leads straight into a HighLevel sub-account — no Zapier. [Developers & AI](/developers/)Exact names for settings, data, hooks and the booking-button contract.
# Developers & AI
> Exact names and contracts for building on GymMark — for developers and AI agents.
These pages give exact names: option keys, post meta, hooks, REST routes and HTML contracts. Where the Help Center says “Forms → Settings → Forms mode”, these pages say `gymmark_forms_mode`. ## For AI agents [Section titled “For AI agents”](#for-ai-agents) * [`/llms.txt`](/llms.txt) lists every public page. * [`/llms-full.txt`](/llms-full.txt) is the whole public site as one Markdown file. * [`/llms-small.txt`](/llms-small.txt) is a condensed version. Every factual statement on these pages is listed in the GymMark claims registry with how and when it was verified. If you find a statement that doesn’t match the plugin’s behavior, treat the plugin as the source of truth and report the page. ## Reference [Section titled “Reference”](#reference) * [Booking-button contract](/developers/booking-button-contract/): the `data-gm-*` attributes that open the pop-out with a class pre-filled.
# Booking-button contract
> The HTML attributes that open GymMark's booking pop-out and pre-fill the class, program, location and form.
Any element with `data-gm-form="1"` opens the GymMark booking pop-out when clicked. Optional `data-gm-*` attributes pass context: which form to show, and which program, location and class session the visitor clicked. ## Attributes [Section titled “Attributes”](#attributes) | Attribute | Value | Effect | | --------------------- | -------------------------------- | ----------------------------------------------------------------------------------------------- | | `data-gm-form` | `1` | Required. Marks the element as a booking trigger. | | `data-gm-form-id` | `gym_form` post ID | Show this form instead of the resolved one. | | `data-gm-offer-id` | `gym_offer` post ID | Show this offer in the pop-out. | | `data-gm-program-id` | Program (`classes`) post ID | Context for form, offer and review matching. | | `data-gm-location-id` | `locations` post ID | Context for form, offer and review matching. | | `data-gm-program` | Program name | Pre-selects the program field; part of the selected session. | | `data-gm-day` | Display text, e.g. `Fri, Oct 16` | Part of the selected session. | | `data-gm-time` | Display text, e.g. `6 a.m.` | Part of the selected session. | | `data-gm-coach` | Instructor name | Part of the selected session. | | `data-gm-hide-coach` | `1` | The source hid instructors: the selected session and its **Change** picker leave the coach out. | The day, time, program and coach values fill the form’s **Selected session** field (when the form has one) and are submitted with the lead. ## Generating a trigger in PHP [Section titled “Generating a trigger in PHP”](#generating-a-trigger-in-php)
```php
echo gymmark_form_trigger_button(
'Book a free class', // label (defaults to "Get started")
array(
'program_id' => 123,
'location_id' => 456,
'program' => 'Barre',
'day' => 'Fri, Oct 16',
'time' => '6 a.m.',
'coach' => 'Jane Coach',
),
'my-extra-class' // optional extra CSS class
);
```
It outputs an `` link. Empty values are left out. The context keys are `program_id`, `location_id`, `form_id`, `offer_id`, `program`, `day`, `time`, `coach` and `hide_coach`. ## Generic triggers [Section titled “Generic triggers”](#generic-triggers) When **Button takeover** is on (option `gymmark_forms_generic_triggers`, default on), these also open the pop-out: `a[href$="#start"]`, `a[href$="#book"]`, `.start` and `.book`. They carry no context unless you add `data-gm-*` attributes. When it’s off, only `[data-gm-form="1"]` opens the pop-out. Triggers do nothing for visitors who can’t see forms; see [Forms mode](/help/forms/forms-mode/) (option `gymmark_forms_mode`: `off`, `admins`, `live`).
# Forms mode
> Turn GymMark booking forms off, test them privately on a live site, or switch them on for everyone.
Forms mode decides who can see and use GymMark’s booking forms: the pop-out, the Booking Form block, and the buttons that open them. Use it to build and test forms on a live site without visitors seeing them. You’ll find it at **Forms → Settings → Forms mode**. ## The three modes [Section titled “The three modes”](#the-three-modes) | Mode | Who sees GymMark forms | Use it when | | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------- | | **Off** | Nobody. GymMark adds no pop-out, no inline forms and doesn’t take over any buttons. Other form plugins (for example Forminator) keep working as before. | The site doesn’t use GymMark forms, or not yet. | | **Test mode** | Only logged-in users who can edit posts (Contributors, Authors, Editors and Administrators). Logged-out visitors see the site exactly as before and can’t submit a GymMark form. | You’re building or checking forms on a live site. | | **Live** | Everyone. | You’ve tested the form and its integrations. | In **Test mode**, an amber “Test mode” label appears above the pop-out, so you can tell at a glance that the public can’t see it. ## What happens when you update the plugin [Section titled “What happens when you update the plugin”](#what-happens-when-you-update-the-plugin) The first time GymMark 2.2.0 or later runs on a site, it picks a mode for you: * If the site already has a published GymMark form, Forms mode starts at **Live**, so nothing changes for that site. * Otherwise it starts at **Off**, so an update never takes over buttons on a site that uses another form plugin. On a multisite network, each site gets its own Forms mode. ## Button takeover [Section titled “Button takeover”](#button-takeover) Under Forms mode there’s a **Button takeover** checkbox. * **On:** any link to `#start` or `#book`, and any button with the class `start` or `book`, opens the GymMark pop-out. * **Off:** those links and buttons are left alone. Only GymMark’s own booking buttons, and schedule or card blocks set to **Integrated form**, open the pop-out. Turn it off while some of the site’s buttons still open another popup, such as a Forminator form. ## Test a form privately on a live site [Section titled “Test a form privately on a live site”](#test-a-form-privately-on-a-live-site) 1. Go to **Forms → Settings**, set **Forms mode** to **Test mode** and click **Save**. 2. Build or edit your form under **Forms**. 3. If you put a GymMark booking button or form on a page to test it, use a **Private** page. See the limitation below. 4. While logged in, open the page, click the button and submit a test lead. 5. When it works, set **Forms mode** to **Live**, then clear your site’s page cache so visitors get the pop-out. ## Limitations [Section titled “Limitations”](#limitations) * **Booking Button and Offer block buttons still show when forms are hidden.** In **Off** mode, and in **Test mode** for logged-out visitors, these buttons appear but don’t open anything, because the pop-out isn’t on the page. Don’t place these blocks on public pages until Forms mode is **Live**. *(Known issue, to be fixed.)*
# What GymMark adds to your site
> A tour of GymMark's parts and where to find their settings in WordPress.
GymMark is a WordPress plugin for gyms and studios. It adds the content types, blocks and settings a gym website needs, and keeps them connected: a class on your schedule knows its program, its instructor and its location, and the booking form that opens from it knows all three. ## The parts [Section titled “The parts”](#the-parts) | Part | What it’s for | Where to find it in WordPress | | -------------------- | --------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | | **Locations** | Each gym or studio, with address, hours and its class schedule. | **Locations** menu | | **Programs** | The classes you offer. | **Classes** menu on a new install. Rename it (for example to “Programs”) in **Settings → Gymmark → Configuration**. | | **Instructors** | Coaches, linked to the classes they teach. | **Instructors** menu (also renameable in **Settings → Gymmark → Configuration**) | | **Events** | One-off and recurring events. | **Events** menu | | **Reviews** | Google reviews, imported and kept in sync, which you can tag and show in blocks. | **Reviews** menu, and **Settings → Gymmark → Reviews** | | **Forms and offers** | Booking and enquiry forms, a pop-out that shows a matching review and offer, and where leads go. | **Forms** menu | | **Blocks** | Schedules, class and instructor cards, review carousels, location finder, booking buttons and more. | In the block editor, under the **GymMark** category | ## Before you start [Section titled “Before you start”](#before-you-start) * **Settings → Gymmark** holds your license key, what you call Programs and Instructors, class levels, events, the Google reviews connection and search (schema) settings. * **Forms → Settings** controls whether booking forms are switched on for visitors. Read [Forms mode](/help/forms/forms-mode/) before turning forms on for an existing site.
# Connect HighLevel
> Send GymMark form leads straight into a HighLevel (LeadConnector) sub-account, without Zapier.
GymMark can send each form lead directly to a HighLevel sub-account. It replaces the usual chain of form → webhook → Zapier → LeadConnector with one step. ## What happens to each lead [Section titled “What happens to each lead”](#what-happens-to-each-lead) 1. **The contact is created or updated.** HighLevel matches existing contacts by email or phone, following the sub-account’s duplicate-contact setting, so a returning lead updates their contact instead of creating a second one. 2. **Tags are added.** They’re added to the contact’s existing tags; nothing is removed. 3. **A note is added** with every answer, the selected class session and the page the lead came from. 4. **Optional:** the contact is added to a workflow. 5. **Optional:** an opportunity named “*contact name* – *program*” is opened in a pipeline stage you choose. If step 1 fails (for example, HighLevel is down), the lead is kept and retried every hour, up to 5 times. Steps 2–5 are not retried, so a retry never creates duplicate notes, workflow runs or opportunities. A lead with neither an email nor a phone number is skipped, because HighLevel can’t match it to a contact. ## Before you start [Section titled “Before you start”](#before-you-start) You need admin access to the HighLevel **sub-account** (the gym’s own location, not the agency) and to the WordPress site. ## 1. Create a Private Integration in HighLevel [Section titled “1. Create a Private Integration in HighLevel”](#1-create-a-private-integration-in-highlevel) 1. In the sub-account, go to **Settings → Private Integrations** and click **Create new Integration**. 2. Name it, for example “GymMark website”. 3. Select these scopes: * `contacts.write` and `contacts.readonly`: contact, tags, note and workflow enrollment * `opportunities.write` and `opportunities.readonly`: pipeline opportunity * `workflows.readonly`: the workflow list * `locations/customFields.readonly`: the custom field list 4. Create it and **copy the token straight away**. HighLevel doesn’t show it again. Also note the sub-account’s **Location ID**. It’s in the browser address bar inside the sub-account: `…/location//…`. ## 2. Connect it in WordPress [Section titled “2. Connect it in WordPress”](#2-connect-it-in-wordpress) 1. Go to **Forms → Integrations**, find the **HighLevel (LeadConnector)** card. 2. Paste the token into **Private Integration token** and the ID into **Location ID**. 3. Tick **Enabled**. 4. Click **Save & test connection**. It reports how many pipelines, workflows and custom fields it can read, and names any scope that’s missing. Then set the options that apply to every lead: | Setting | What it does | | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | | **Contact source** | Shown as the contact’s Source. Leave it blank for “Website – *form name*”. | | **Tags** | Comma-separated tags added to every lead. | | **Context tags** | Also tags the lead with the program and location they enquired about. On by default. | | **Submission note** | Adds the note described above. On by default. | | **Add to workflow** | Picks a workflow from the sub-account. Or paste an ID into **…or workflow ID**. A `workflow_` prefix copied from Zapier is removed automatically. | | **Create opportunity in** | Picks the pipeline stage for new opportunities, or none. | ## 3. Turn it on for a form and map its fields [Section titled “3. Turn it on for a form and map its fields”](#3-turn-it-on-for-a-form-and-map-its-fields) Each form chooses where its leads go. 1. Open the form under **Forms** and go to its **Integrations** tab. 2. Switch **HighLevel** on. 3. Check the **Field mapping** table. For each form field, **Send to** is one of: * **Auto**: matched by name or type (it shows what it will match, for example “Auto → Email”). Fields it can’t match go into the note. * **A contact field**, such as First name, Phone or Postal code. * **A custom field** from the sub-account. * **Note only**: in the note, not on the contact card. * **Don’t send**: left out of HighLevel entirely. 4. Optionally add **Extra tags**, or choose a different **Workflow** or **Opportunity** for this form. 5. Click **Publish** to save. New fields you add in the **Fields** tab appear in the mapping table straight away. Turn off Zapier for this form If the form also sends to a Zapier webhook that creates the same contact, each lead reaches HighLevel twice. Switch the form’s **Webhook** off in the same Integrations tab once HighLevel is working. ## 4. Check it worked [Section titled “4. Check it worked”](#4-check-it-worked) Submit a test lead, then open **Forms → Integrations** and expand **Recent deliveries** on the HighLevel card. It lists the last 10 leads, each with a link to the contact in HighLevel and any warnings. Testing on a live site before launch? Use [Test mode](/help/forms/forms-mode/). ## “Email is not verified” in HighLevel [Section titled ““Email is not verified” in HighLevel”](#email-is-not-verified-in-highlevel) HighLevel shows this badge until its own email-verification service has checked the address. A confirm-email field on the form doesn’t change it, and GymMark can’t set it. Leads still arrive and workflows still run.