Door & Gate Access
Core Platformdoor_access_auto_messagesDoor & Gate AccessManages the Door & Gate Access automated per-user messaging rules — facility-level rules that automatically set or clear a user's mobile-app message based on their synced membership status, person type, or tags (e.g. show "Your membership is suspended — contact the front desk" to everyone whose status is Suspended).
Manual per-user messages (set via door_access_manage_users) and decoupled/suppressed users are never overwritten by automation.
Actions:
get— the current config: { enabled, rules: [{ id, enabled, match: { field membershipStatus|type|tag, values[] }, message: { title, text, severity info|warning|critical, link } }] }update— save the config (configobject, full replace — call get first and send back the whole modified document). Saving immediately queues a BACKGROUND sweep that re-evaluates every User Access row at the facility; the response confirms { sweep: { status: 'queued' } }.sweep_status— progress of that background sweep: { status queued|running|done|error, startedAt, finishedAt, usersScanned, usersUpdated, startedBy, error }. Poll this after an update to confirm the sweep finished.
Parameters
| Name | Type | Description |
|---|---|---|
action* | "get" | "update" | "sweep_status" | `get` = read config; `update` = save config + queue re-evaluation sweep; `sweep_status` = background sweep progress. |
facilityId | string | Facility ID. Falls back to the currently selected facility when omitted. |
config | object | update: the full config { enabled, rules: [{ id, enabled, match: { field, values }, message: { title, text, severity, link } }] }. Full replace — get the current config first. |
door_access_configure_doorDoor & Gate AccessConfigures a Door & Gate Access (DGA) door controller: per-door rule/geofence overrides plus device-side settings. Only the fields you supply are changed; everything else is left untouched.
Configurable fields:
accessRulesOverride— per-door access rules object overriding the facility defaults. Passnullto clear and fall back to facility rules.geofenceOverride— per-door geofence override (enabled, center {lat,lng}, radiusMeters, accuracyBufferMeters...). Passnullto clear.icon— door | door-sliding | door-double | garage | gate-barrier | pedestrian | security | office | delivery | lockerholdTimeSeconds— relay hold time on unlock, 1-30 secondsmaintenanceMode— off | force-open (relay latched open) | force-closed (locked; only admin override unlock works)doorLocation— free-text physical location label, max 64 chars; empty string clearsplayNotificationSoundOnRelayToggle— boolean; whether the controller chirps when the relay togglesautoUnlockSchedule— weekly schedule that latches the relay open during configured windows:{ enabled, windows: { monday: [{ from: "09:00", to: "17:00" }], ... } }in the facility's timezone. Cross-midnight windows are split automatically. Passnullto clear.
When device-side fields change, the tool also pushes the matching Balena env vars and an MQTT refresh so the controller picks up the new config immediately (best-effort; a Balena outage will not fail the save — note that an autoUnlockSchedule change triggers a container restart on the controller).
SAFETY: maintenanceMode force-open latches the door physically open and autoUnlockSchedule keeps it unlocked during the windows. The AI assistant MUST confirm with the user before applying these unless explicitly requested.
Workflow: get the doorId (and current settings) from door_access_doors first.
Parameters
| Name | Type | Description |
|---|---|---|
facilityId | string | Facility ID. Falls back to the currently selected facility when omitted. |
doorId* | string | Door controller ID (balena UUID). Get it from door_access_doors (action list). |
accessRulesOverride | objectnull | Per-door access rules override. null clears the override. |
geofenceOverride | objectnull | Per-door geofence override. null clears the override. |
icon | "door" | "door-sliding" | "door-double" | "garage" | "gate-barrier" | "pedestrian" | "security" | "office" | "delivery" | "locker" | Icon shown in the admin list and the mobile app. |
holdTimeSeconds | integer | Relay hold time on unlock, in seconds. |
maintenanceMode | "off" | "force-open" | "force-closed" | Maintenance override. force-open latches the relay open; force-closed blocks member unlocks. |
doorLocation | stringnull | Physical location label (max 64 chars). Empty string or null clears. |
playNotificationSoundOnRelayToggle | boolean | Whether the controller plays a chirp when the relay toggles. |
autoUnlockSchedule | objectnull | Weekly auto-unlock schedule { enabled, windows: { day: [{from,to}] } } (HH:MM, facility timezone). null clears. |
door_access_control_doorDoor & Gate AccessPhysically actions a Door & Gate Access (DGA) door controller: admin override unlock, or an identify beep to locate the device on-site.
This is the DGA module (Performance Hub managed door controllers), NOT the GymMaster partner integration — for partner-integration doors use facility_control_door instead.
Actions:
unlock— admin override unlock. Briefly toggles the relay using the door's configured hold time (the door auto-relocks). Writes anadmin-overrideaudit row to the Access Logs attributed to the acting user. Always works, even when the door is in force-closed maintenance mode (this is the admin escape hatch). Requires areasonfor the audit trail.identify— plays a short locator beep pattern (3 beeps, ~2 seconds) on the door controller so someone on-site can find it. Diagnostic only; no audit row.
SAFETY: unlocking a door is a physical security action. The AI assistant MUST confirm with the user before unlocking unless the user explicitly and unambiguously asked to unlock that specific door.
Workflow: if you don't know the doorId, call door_access_doors with action list first — do NOT guess door IDs.
Returns (unlock): commandId, the audit eventId, holdTimeSeconds used, and whether force-closed maintenance mode was bypassed. The command is dispatched fire-and-forget; device confirmation is not awaited.
Parameters
| Name | Type | Description |
|---|---|---|
action* | "unlock" | "identify" | `unlock` = admin override unlock (auto-relocks); `identify` = locator beep. |
facilityId | string | Facility ID. Falls back to the currently selected facility when omitted. |
doorId* | string | Door controller ID (balena UUID). Get it from door_access_doors (action list). |
reason | string | Reason for the unlock, stored in the audit log. REQUIRED for unlock. If the user did not state one, generate a professional reason from the conversation context. |
door_access_doorsDoor & Gate AccessRead-only tool for the Door & Gate Access (DGA) module: lists the door controllers at a facility and fetches a single door's live device status.
This is the DGA module (mobile-app door unlocking managed in Performance Hub), NOT the GymMaster partner integration — for partner-integration doors use facility_control_door instead.
Actions:
list— every door controller at the facility with: doorId, doorName, doorLocation, icon, live online state (heartbeat-derived), maintenanceMode, holdTimeSeconds, auto-unlock schedule + live scheduled-unlock state, whether the door has rule/geofence overrides, and the fully-merged effective access rules and geofence.device_status— Balena + heartbeat snapshot for one door (requires doorId): update status, IP address, network source, WiFi SSID, hardware make/model/serial, OS/supervisor/application versions, uptime, CPU usage/temperature, memory usage. Returnsdata: nullwhen no telemetry is available.
Use this tool when:
- The user asks which doors exist at a facility, whether a door is online, or what rules/geofence apply to a door
- You need a doorId before calling door_access_control_door or door_access_configure_door
- Troubleshooting an offline or misbehaving door controller (device_status)
Note: adding/decommissioning door controllers is done via Device Management, not this module.
Parameters
| Name | Type | Description |
|---|---|---|
action* | "list" | "device_status" | `list` = all doors at the facility; `device_status` = telemetry snapshot for one door. |
facilityId | string | Facility ID. Falls back to the currently selected facility when omitted. |
doorId | string | Door controller ID (balena UUID). Required for device_status. Get it from the list action. |
door_access_eventsDoor & Gate AccessRead-only tool for the Door & Gate Access (DGA) module's Access Logs — the audit trail of every unlock attempt at a facility.
Returns door-access events newest-first: member unlocks from the mobile app, denied attempts (with the denial reason — rule mismatch, outside hours, geofence, access disabled, etc.), admin override unlocks performed from Performance Hub or via MCP, and command outcome states (dispatched / confirmed / failed / timed out).
Each event includes the doorId, who attempted it (personID — a User Access userId for members, or the admin's email for admin overrides), the state/outcome, timestamps, and request metadata (source, geolocation result, user agent).
Parameters:
from/to— unix-seconds time window (optional)limit— max events to return (default 100, capped at 500)- The response includes
lastEvaluatedKeywhen more pages exist; narrow the time window to page through history.
Use this tool when:
- The user asks who unlocked a door, when, or why an unlock was denied
- Auditing admin override unlocks
- Investigating door activity over a time period
Parameters
| Name | Type | Description |
|---|---|---|
facilityId | string | Facility ID. Falls back to the currently selected facility when omitted. |
from | integer | Window start (unix seconds). Optional. |
to | integer | Window end (unix seconds). Optional. |
limit | integer | Maximum events to return (default 100, max 500). |
door_access_invitationsDoor & Gate AccessBulk Door & Gate Access mobile-app invitations: emails users a link to install and sign in to the app. Two-step preview -> send flow.
(For a single user, use door_access_manage_users with action resend_invite instead.)
Actions:
preview— computes the candidate recipients WITHOUT sending anything. Base pool: every non-deleted user with an email who has never used the app. By default it is narrowed to users who currently have access and have never been invited; widen with:includeAlreadyInvited: true— also re-send to previously-invited usersincludeNoAccess: true— also include users without effective access
Returns counts, a sample, and the full candidateuserIdslist for the send step.
send— emails the invitation to the supplieduserIds(max 50 per call; batch a longer preview list across multiple calls). Each user is re-validated at send time — rows that vanished, lost their email, or logged in since the preview are skipped, not failed. Returns per-user outcomes (sent / failed / skipped) and a summary.
SAFETY: send emails real users. ALWAYS run preview first, show the user the recipient count, and get their confirmation before calling send.
Parameters
| Name | Type | Description |
|---|---|---|
action* | "preview" | "send" | `preview` = dry-run candidate computation; `send` = email the invitations. |
facilityId | string | Facility ID. Falls back to the currently selected facility when omitted. |
includeAlreadyInvited | boolean | preview: also include users who already received an invitation (re-send). |
includeNoAccess | boolean | preview: also include users who do not currently have effective access. |
userIds | array | send: the recipients (from the preview), max 50 per call. |
door_access_manage_usersDoor & Gate AccessWrite tool for the Door & Gate Access (DGA) module's User Access list — creates, edits, and removes the people who can use the Door & Gate Access mobile app at a facility, and manages their grants, sync coupling, sessions, and invitations.
Use door_access_users (read tool) first to find userIds and inspect current state.
Actions:
create— manually add a person (userobject; at least one of mobilePhone/email required; phone is normalised to E.164).sendInvite: truealso emails the app invitation. Returns 409 with the existing user when phone/email already exists at the facility.bulk_create— import up to 200 people in one call (usersarray, same fields as create plus optionalaccessEnabledandgrants). Per-row outcomes are reported individually (created / duplicate / error).update— partial edit of any user fields (userId+userobject). Pass null to clear an optional field. IncludesaccessEnabled(the access kill switch),autoMessageSuppressed, and the per-userappMessage.delete— soft-delete a user (userId). Access logs are preserved. Confirm with the user before deleting.set_grants— REPLACE the user's personal grants (userId+grants: { facilityWide, doors[], groups[], accessHours? }). Grants work alongside facility rules — a user may unlock when either matches.decouple— freeze the row against partner/CCTV sync updates (admin takes manual control).recouple— re-attach the row to sync updates.logout— revoke all of the user's mobile-app sessions (refresh tokens, matched by their phone/email).resend_invite— force-send the app invitation email (requires the user to have an email on file).
User object fields (create / bulk_create rows / update): firstName, lastName, mobilePhone, email, type, membershipStatus, tags[], customTags[], notes, accessEnabled, autoMessageSuppressed, appMessage ({ enabled, title, text, severity info|warning|critical, link {url,label}, startsAt, endsAt }).
Parameters
| Name | Type | Description |
|---|---|---|
action* | "create" | "bulk_create" | "update" | "delete" | "set_grants" | "decouple" | "recouple" | "logout" | "resend_invite" | The operation to perform. |
facilityId | string | Facility ID. Falls back to the currently selected facility when omitted. |
userId | string | User Access row ID. Required for update, delete, set_grants, decouple, recouple, logout, resend_invite. |
user | object | User fields for create / update (firstName, lastName, mobilePhone, email, type, membershipStatus, tags, customTags, notes, accessEnabled, autoMessageSuppressed, appMessage). |
users | array | bulk_create rows (max 200). Same fields as user, plus optional grants { facilityWide, doors[], groups[] }. |
sendInvite | boolean | create / bulk_create: also send the app invitation email to users with an email address. |
grants | object | set_grants: REPLACES the grants — { facilityWide: boolean, doors: string[], groups: string[], accessHours?: object }. |
door_access_scopesDoor & Gate AccessManages Door & Gate Access "Access Scopes" (door groups) at a facility — named sets of doors that can carry their own access rules and geofence overrides, and that users can be granted access to via door_access_manage_users (set_grants with groups).
A door can belong to multiple scopes. At unlock time the evaluator merges rules in priority order: door override > scope override > facility defaults.
Actions:
list— all scopes at the facility (name, description, doorIds, whether they carry rule/geofence overrides)get— one scope by groupIdcreate— new scope.namerequired (max 120 chars); optional description (max 2000), doorIds[] (from door_access_doors), accessRulesOverride, geofenceOverrideupdate— partial edit (groupId + any of the same fields). doorIds REPLACES the door list when supplied.delete— soft-delete a scope (groupId). Users granted access via this scope lose that grant path. Confirm with the user before deleting.
Parameters
| Name | Type | Description |
|---|---|---|
action* | "list" | "get" | "create" | "update" | "delete" | The operation to perform. |
facilityId | string | Facility ID. Falls back to the currently selected facility when omitted. |
groupId | string | Scope (door group) ID. Required for get, update, delete. |
name | string | Scope name (required for create, max 120 chars). |
description | stringnull | Optional description (max 2000 chars). null/empty clears. |
doorIds | arraynull | Door controller IDs in this scope. REPLACES the list when supplied; null empties it. |
accessRulesOverride | objectnull | Scope-level access rules override. null clears. |
geofenceOverride | objectnull | Scope-level geofence override. null clears. |
door_access_settingsDoor & Gate AccessReads and writes the facility-level Door & Gate Access settings (the module's Settings tab), and dry-runs geofence evaluations.
Actions:
get_rules— the facility's effective rules document:memberTypeRules/tags— who can access (matched against User Access type/status/tags)accessHours— operating-hours windows per daygeofence— { enabled, center {lat,lng}, centerOverridden, radiusMeters, accuracyBufferMeters, requireAccuracyBetterThan }appLock— facility-enforced mobile-app re-authentication { enforced, graceSeconds (0-3600), allowedMethods [biometric|pin] }supportContacts— { mode: default|on|off } controls whether the app shows the club's support email/phoneappMessage— facility-wide app announcement { enabled, title, text, severity info|warning|critical, link {url,label}, startsAt, endsAt (unix seconds) }
update_rules— saves the rules document (rulesobject). This is a FULL replace of memberTypeRules / tags / accessHours / geofence — always call get_rules first, modify the result, and send the whole document back. Changing these affects who can open doors facility-wide; confirm significant changes with the user.test_geofence— dry-run a geofence evaluation for a coordinate (lat,lng, optionalaccuracyMetersanddoorIdto include that door's override). Returns the effective geofence and the pass/fail result — nothing is modified.
Sync settings live in door_access_sync; automated per-user messages in door_access_auto_messages.
Parameters
| Name | Type | Description |
|---|---|---|
action* | "get_rules" | "update_rules" | "test_geofence" | The operation to perform. |
facilityId | string | Facility ID. Falls back to the currently selected facility when omitted. |
rules | object | update_rules: the full rules document (memberTypeRules, tags, accessHours, geofence, appLock, supportContacts, appMessage). Get it with get_rules first and send the whole modified document. |
lat | number | test_geofence: latitude of the test pin. |
lng | number | test_geofence: longitude of the test pin. |
accuracyMeters | number | test_geofence: reported GPS accuracy in metres (optional). |
doorId | string | test_geofence: evaluate against this door's geofence override instead of just the facility default (optional). |
door_access_syncDoor & Gate AccessManages Door & Gate Access User Access syncing — pulling people in from the facility's partner integrations (member-management systems) and CCTV — plus the partner-synced bulk-delete flow and the sync troubleshooting log.
Actions:
partners— the partner integrations at the facility (partnerId, name, enabled, how many User Access rows each has synced)get_config— the sync config: { partner: { enabled, requireIdentifier email|phone|both|either, partners: per-partner opt-out map }, cctv: { enabled, requireIdentifier } }, plus lastRunAt / lastRunCountsupdate_config— save the admin-controlled sync config fields (configobject, same shape; lastRunAt/lastRunCounts are engine-written and ignored)run— run an on-demand sync now (also runs nightly). Synchronously sweeps every enabled source and returns per-source counts (processed / created / updated / skipped)log— recent sync outcomes newest-first with the reason a member did or didn't sync (limitdefault 100, max 500)bulk_delete_preview— dry-run of deleting partner-synced users (optionally scoped to onepartnerId). Returns the affected count, a sample, and aconfirmToken. Nothing is modified.bulk_delete— execute the soft-delete. Requires theconfirmTokenfrom the preview; if the user set changed since the preview the call is rejected (409) and you must re-preview. Access logs are preserved, but admin customisations on the deleted rows (grants, custom tags, notes) are lost.
SAFETY: bulk_delete removes potentially thousands of users' app access. ALWAYS run bulk_delete_preview first, show the user the count, and get their explicit confirmation before calling bulk_delete.
Parameters
| Name | Type | Description |
|---|---|---|
action* | "partners" | "get_config" | "update_config" | "run" | "log" | "bulk_delete_preview" | "bulk_delete" | The operation to perform. |
facilityId | string | Facility ID. Falls back to the currently selected facility when omitted. |
config | object | update_config: the sync config { partner: { enabled, requireIdentifier, partners }, cctv: { enabled, requireIdentifier } }. |
partnerId | string | bulk_delete_preview / bulk_delete: limit to one partner. Omit to target all partner-synced users. |
confirmToken | string | bulk_delete: the token returned by bulk_delete_preview. REQUIRED. |
limit | integer | log: max entries to return (default 100, max 500). |
door_access_usersDoor & Gate AccessRead-only tool for the Door & Gate Access (DGA) module's User Access list — the people who can sign in to the Door & Gate Access mobile app at a facility.
Actions:
list— all (non-deleted) User Access rows at the facility, each with a computedhasAccessflag (would they pass the effective access rules / grants). Supports filters:search— free text across name, phone, email, tagssource— all | manual | partner | cctv (where the row came from)accessEnabled— any | true | false (the per-user kill switch)hasGrants— any | true | false (has personal grants)hasAccess— any | true | false (computed effective access)
get— one User Access row by userId (full detail: identity, tags, grants, sync linkage, invitation status, app sessions, per-user app message).people_config— the distinct person types, membership statuses, and tags present at the facility (useful for building access rules that match real data).
Use this tool when:
- The user asks who can unlock doors, whether a specific person has access, or why someone does/doesn't have access
- You need a userId before calling door_access_manage_users
- Building or reviewing access rules and you need the real type/status/tag values (people_config)
Parameters
| Name | Type | Description |
|---|---|---|
action* | "list" | "get" | "people_config" | `list` = filtered user list; `get` = single user; `people_config` = distinct types/statuses/tags. |
facilityId | string | Facility ID. Falls back to the currently selected facility when omitted. |
userId | string | User Access row ID. Required for the get action. |
search | string | list filter: free-text search across name, phone, email, and tags. |
source | "all" | "manual" | "partner" | "cctv" | list filter: where the row came from. Default all. |
accessEnabled | "any" | "true" | "false" | list filter: per-user access kill switch state. Default any. |
hasGrants | "any" | "true" | "false" | list filter: whether the user has personal grants. Default any. |
hasAccess | "any" | "true" | "false" | list filter: computed effective access (rule match or grant). Default any. |
hideExcluded | boolean | list filter (legacy): hide synced users with no effective access; the response then includes a hidden { count, sample } envelope. Prefer the hasAccess filter. |