TapHome
Community Beta Sensors

Glofox room bookings — room preparation

Glofox facility bookings in TapHome over the Glofox cloud REST API (HTTPS), one module per room: Booked now, Minutes to next booking and a Prepare room flag that switches on a set time before each booking. Smart rules use the flag to heat or cool the room and switch lights and audio on before a trainer arrives, and off when the booking ends. Read-only; needs Live API access requested by the studio's superadmin.

GlofoxPacket Parser → HTTP3 devicesRevision 1.0 Last updated: November 10, 2026

Submitted by Matej Hudec

Glofox room bookings — room preparation

Import into the TapHome appHardware › Packet Parser › Add from template

Download Template
On this page

Integration guide

Glofox is a cloud booking platform of ABC Fitness, so there is nothing to wire: the TapHome Core reads the room bookings from the Glofox REST API over HTTPS. Setup on the studio side has three parts — Glofox API access for the studio, every rented room set up as a bookable facility, and the studio’s Branch ID and time zone. The template is then imported once per room.

Screenshots are from a Glofox Sandbox test location (ABC Fitness Developer Portal and Glofox dashboard, October 2026) and from the TapHome app connected to a test control unit with simulated bookings.

Configuration

1. Request Glofox API access

The studio’s superadmin signs up in the ABC Fitness Developer Portal (Account Type Customer) and clicks Request Integrator:

  • Environment: Live (Sandbox is only a test location with demo data).
  • Namespace: the studio’s Glofox namespace.
  • Intended Use Case: for example “TapHome building automation reads facility bookings to prepare rooms (heating, lights, audio) before each booking. Read-only.”

ABC Fitness reviews Live requests and answers by e-mail. The API key and API token are then requested with Request credentials and arrive at the portal e-mail address. Questions about access: [email protected].

Request access guide in the ABC Fitness Developer Portal

The API key and the API token give access to the studio’s Glofox data. Glofox asks to use them only in backend integrations, never in client-side applications; in TapHome they belong only in the template’s import parameters.

2. Make every room a bookable facility

First switch on Settings → Booking → Facility Rental; the facility switches below appear only after that. Then Manage → Facilities lists the rooms. Each room the trainers rent must be a facility with Bookable switched on, a slot schedule (Schedule tab) and, under Price / Eligibility, Drop In Clients or Clients allowed (a price of 0 is fine). Without them a trainer’s booking ends with “Membership not allowed”.

Facilities in the Glofox dashboard
Facility detail with Public and Bookable switched on

3. Find the Branch ID and the studio time zone

With the API key and token, one request returns the studio location, its ID and its time zone:

1
2
3
curl -s -X POST https://gf-api.aws.glofox.com/prod/v3.0/locations/retrieve \
  -H "x-api-key: <API key>" -H "x-glofox-api-token: <API token>" \
  -H "Content-Type: application/json" -d '{}'

data[]._id is the BranchId, data[].address.timezone_id the studio time zone (for example Europe/Bratislava). Without a filter the request returns every location in the namespaces the integration may read, so with several studios pick the entry of the studio whose rooms the modules watch.

The script clock of the TapHome Core must be in the studio time zone: on Core 2025.2 it is the control unit’s system time zone, on newer Core the time zone of the TapHome location.

4. Import the template once per room

In the TapHome app open the Packet Parser interface (add it first if the control unit has none), then Add from template → Add from file and pick the downloaded template. Fill in ApiKey, ApiToken, BranchId, LeadMinutes and the room’s FacilityId.

Put the room name into Device name prefix and end it with a separator, for example Yoga room - . The prefix is joined to the device names exactly as typed, so without the separator a device is called “Yoga roomBooked now”. Zone puts the room’s three devices into the room’s zone.

Import parameters in the TapHome app

If the Facility ID is not known yet, enter any text. After the first poll the module reports an error that lists every facility of the branch with its ID (Devices → Errors and warnings → the module):

Module error listing the facilities of the branch

Copy the right ID into the module’s FacilityId variable (or import again) and repeat for every room.

5. Check against the Glofox calendar

Bookings of a room appear in the Glofox Calendar (filter Facilities). With a booking from 10:00 to 11:00 and LeadMinutes 60, the room’s values are:

TimeBooked nowMinutes to next bookingPrepare room
8:30Free90Idle
9:00Free60Prepare
10:30Bookednext booking or 1440Prepare
11:00Freenext booking or 1440Idle

Minutes are whole minutes counted from the poll, so a value can differ from the table by one.

Facility booking in the Glofox calendar

In the TapHome app each room shows the three values under its module:

Room values in the TapHome app

Smart rules

Prepare room is the condition of the room’s smart rules: heating or cooling setpoint, lights and audio scene while it is Prepare, everything off when it returns to Idle. Booked now suits occupancy displays; Minutes to next booking lets a rule start a slow heating system earlier than LeadMinutes.

Available devices 3

These devices appear in TapHome after import.

Glofox Room Module

Glofox Room

Read (module)
# Bookings of this room (FacilityId) that run now or start within 24 h. Glofox times are the studio's local time
# ("yyyy-MM-dd HH:mm:ss"), so the Core's time zone must be the studio's. Texts in that format sort like the times,
# which lets JSONPath filters compare them directly.
VAR now := NOW();
VAR nowText := TOSTRING(now, "yyyy-MM-dd HH:mm:ss");
VAR leadText := TOSTRING(DATETIMEADD(now, 0, 0, 0, 0, LeadMinutes), "yyyy-MM-dd HH:mm:ss");
VAR nowLocal := ROUND(now.UNIXTIME + LOCALTIMEZONE() / 1000);
VAR hKey := "x-api-key: " + ApiKey;
VAR hToken := "x-glofox-api-token: " + ApiToken;
VAR hBranch := "x-glofox-branch-id: " + BranchId;
VAR msg := "";
VAR i := 0;
VAR fid := "";

# Every failed poll ends in RETURN before the values are written. After five failed polls in a row (Glofox or the
# internet down) the room is released, so an old "Prepare" does not keep heating and lights on.
failCount := failCount + 1;
IF failCount > 5
    bookedNow := 0;
    prepareRoom := 0;
    minsToNext := 1440;
END

# After start and then once an hour: the configured room must exist; if not, the error lists the rooms of the branch
# with their ids. facilityCheck counts the polls until the next check.
IF facilityCheck <= 0
    VAR fresp := SENDHTTPREQUEST("/prod/3.0/locations/" + BranchId + "/facilities?limit=100", "GET", "", hKey, hToken, hBranch);
    IF !fresp.IsSuccess
        # no connection: StatusCode 0 and no ReasonPhrase
        IF !ISNULL(fresp.ReasonPhrase)
            msg := " " + fresp.ReasonPhrase;
        END
        ADDERROR(1, "Glofox API: HTTP " + TOSTRING(fresp.StatusCode, "F0") + msg);
        RETURN(0);
    END
    IF ISNULL(PARSEJSON(fresp.Content, "$.data", TRUE))
        msg := PARSEJSON(fresp.Content, "$.message", TRUE);
        ADDERROR(2, "Glofox API: " + IF(ISNULL(msg), "unexpected answer", msg));
        RETURN(0);
    END
    IF ISNULL(PARSEJSON(fresp.Content, "$.data[?(@._id == '" + FacilityId + "')].name", TRUE))
        msg := "";
        fid := PARSEJSON(fresp.Content, "$.data[0]._id", TRUE);
        WHILE !ISNULL(fid)
            msg := msg + IF(i = 0, ": ", "; ") + PARSEJSON(fresp.Content, "$.data[" + TOSTRING(i, "F0") + "].name", TRUE) + " = " + fid;
            i := i + 1;
            fid := PARSEJSON(fresp.Content, "$.data[" + TOSTRING(i, "F0") + "]._id", TRUE);
        LOOP
        ADDERROR(3, "Facility '" + FacilityId + "' is not in this branch. Facilities" + msg);
        RETURN(0);
    END
    facilityCheck := 60;
END
facilityCheck := facilityCheck - 1;

VAR path := "/prod/2.2/branches/" + BranchId + "/bookings?status=BOOKED&limit=50&time_finish_start_date=" + TOSTRING(nowLocal, "F0") + "&time_start_end_date=" + TOSTRING(nowLocal + 86400, "F0") + "&page=";
VAR room := "$.data[?(@.model_id == '" + FacilityId + "' && @.status == 'BOOKED' && ";
VAR newBooked := 0;
VAR newPrepare := 0;
VAR nextText := "";
VAR candidate := "";
VAR total := 0;
VAR page := 1;
VAR pages := 1;
VAR nextStart := now;

# limit=50: a booking is about 1 kB (0.9 kB measured) and SENDHTTPREQUEST accepts at most 128 kB; at most 10 pages =
# 500 bookings of the whole branch (class bookings included)
VAR resp := SENDHTTPREQUEST(path + "1", "GET", "", hKey, hToken, hBranch);
WHILE page <= pages
    IF page > 1
        resp := SENDHTTPREQUEST(path + TOSTRING(page, "F0"), "GET", "", hKey, hToken, hBranch);
    END
    IF !resp.IsSuccess
        IF !ISNULL(resp.ReasonPhrase)
            msg := " " + resp.ReasonPhrase;
        END
        ADDERROR(1, "Glofox API: HTTP " + TOSTRING(resp.StatusCode, "F0") + msg);
        RETURN(0);
    END
    # a wrong API token or BranchId answers HTTP 200 with success:false and a message, and no meta
    total := PARSEJSON(resp.Content, "$.meta.totalCount", TRUE);
    IF ISNULL(total)
        msg := PARSEJSON(resp.Content, "$.message", TRUE);
        ADDERROR(2, "Glofox API: " + IF(ISNULL(msg), "unexpected answer", msg));
        RETURN(0);
    END
    pages := MIN(10, CEIL(total / 50));
    IF !ISNULL(PARSEJSON(resp.Content, room + "@.time_start <= '" + nowText + "' && @.time_finish > '" + nowText + "')]._id", TRUE))
        newBooked := 1;
    END
    IF !ISNULL(PARSEJSON(resp.Content, room + "@.time_start <= '" + leadText + "' && @.time_finish > '" + nowText + "')]._id", TRUE))
        newPrepare := 1;
    END
    # earliest booking that has not started: take any later than now, then ask for an earlier one until none is left
    candidate := PARSEJSON(resp.Content, room + "@.time_start > '" + nowText + "'" + IF(nextText = "", "", " && @.time_start < '" + nextText + "'") + ")].time_start", TRUE);
    WHILE !ISNULL(candidate)
        nextText := candidate;
        candidate := PARSEJSON(resp.Content, room + "@.time_start > '" + nowText + "' && @.time_start < '" + nextText + "')].time_start", TRUE);
    LOOP
    page := page + 1;
LOOP
IF total > 500
    ADDWARNING(4, "More than 500 bookings of the branch in the next 24 hours; only the first 500 were read, a booking of this room beyond them is not seen");
END

failCount := 0;
bookedNow := newBooked;
prepareRoom := newPrepare;
IF nextText = ""
    minsToNext := 1440;
ELSE
    nextStart := DATETIME(nextText, "yyyy-MM-dd HH:mm:ss");
    minsToNext := MIN(1440, FLOOR((nextStart.TICKS - now.TICKS) / 60000));
END
Booked now Reed Contact

Booked now

Read
RETURN(bookedNow);
Minutes to next booking Variable

Minutes to next booking

Read
RETURN(minsToNext);
Prepare room Reed Contact

Prepare room

Read
RETURN(prepareRoom);
Connection: Packet Parser → HTTP

Import parameters

Enter these values when you import the XML.

Asked in the appDefault
Glofox API key (x-api-key header)Enter at import
Glofox API token (x-glofox-api-token header)Enter at import
Glofox Branch ID (x-glofox-branch-id header)Enter at import
Glofox Facility ID for this room (model_id in booking response)Enter at import
Lead time in minutes — Prepare Room activates this many minutes before a booking60

Found a problem with this device template?

Tell us what's wrong, what's missing, or how the template should behave. We rely on your feedback to keep the catalog accurate.

Template · TapHome portal Glofox room bookings — room preparation Open this template in the Customer Portal to apply it to one of your homes, or to draft a refinement and submit it back to the catalog. Packet Parser → HTTP View in TapHome portal