ButterflyMX Intercom
Cloud-managed multi-tenant video intercom. ButterflyMX is the dominant smart-intercom platform in modern apartment and mixed-use construction in North America; this driver brings their door-release and access-event API into GEM so a property's lobby / package-room / amenity doors can be triggered from macros, scenes, and access workflows.
What this integration covers
- Door release ("buzz in") from a GEM macro or zone command — drives the BMX cloud's per-door
openendpoint for any door tied to your partner credentials. - Door roster polling — pulls the list of doors visible to the API key on a configurable cadence. You create one GEM zone per door you want to control; the poll then reflects live state onto the zones you created. The driver does not auto-create zones — see Mapping doors to zones.
- Access event tail — walks the recent access-log feed and surfaces last-event metadata (event type, timestamp, who released) onto each door zone.
- Building scoping — if your partner credentials span multiple properties, the optional
building_idattribute scopes a single GEM device row to one building.
What this integration does NOT cover
- SIP audio / video preview of a panel call. ButterflyMX panels speak SIP through their own iOS/Android/web clients; integrating that path requires a SIP UA stack and is out of scope here.
- Resident / tenant provisioning (creating user accounts, mailing PINs, mailing physical keys). The BMX OS admin portal is the right tool for that.
- Door schedule editing (auto-unlock windows). Edit auto-unlock schedules in the BMX OS admin portal; the driver only releases doors on command and reads roster/event state — it does not change a door's schedule.
Prerequisites
- A ButterflyMX partner-tier account with API access enabled. Resident / front-desk credentials do not have API scope — contact your ButterflyMX rep.
- A
client_idandclient_secretpair minted in the BMX partner portal. - Outbound HTTPS reachability from the GEM controller to:
accounts.butterflymx.com(OAuth 2.0 token endpoint)api.butterflymx.com(REST API)
Setup steps
- Have your ButterflyMX partner contact provision API access for the property and mint a
client_id+client_secretpair. - Go to System → Devices → Add Device and pick ButterflyMX Intercom as the driver.
- Enter the API Client ID and API Client Secret on the device row (the secret is encrypted at rest).
- If your credentials span multiple buildings and you want this device row scoped to one, set Building ID Filter to the BMX building UUID. Leave blank to include every visible building. (This filter governs which doors receive live state/event updates —
get_doorsstill lists every door the credentials can see.) - Save the device row. After it connects (a few seconds), the device shows as Connected.
- From the Commands tab, run
get_doorsto enumerate available doors. Each entry'sidis what you'll use as the zone address. - Create one zone per door you want to control — the driver does not create them for you. See Mapping doors to zones for the subsystem to pick and the address to enter.
Mapping doors to zones
The driver does not auto-create zones. The status poll only updates zones that already exist and are bound to this device — discovering a door via get_doors does not add it to GEM on its own. For every door you want to control, create a zone by hand:
- Go to System → Zones → add a zone and set the zone's Device to this ButterflyMX device.
- Set the zone Address to the door's BMX UUID exactly as
get_doorsreturns it (the addresses are case-sensitive). - Pick a Subsystem. The natural choices are Doors (a lobby, amenity, or package-room door) or Gates (a vehicle or pedestrian gate) — those are the subsystem names a stock GEM install ships with. There is no "Locks" subsystem by default.
The New Zone editor only pre-fills the subsystem when a driver's hint matches a subsystem name exactly. ButterflyMX hints gate/door/lock (singular), while the seeded subsystems are Doors and Gates (plural), so nothing is pre-selected for a ButterflyMX zone — pick the subsystem yourself. The choice is organizational (it groups the door in the UI); it does not change how unlock works. The zone's state is set by the driver to idle/released directly, independent of relay-state mapping.
Once the zone exists, an unlock/open command on it releases the door, and the poll loop keeps the zone's state, last_event, last_event_at, and last_event_user attributes current.
Attribute reference
Device attributes
| Name | Type | Required | Description |
|---|---|---|---|
client_id | string | yes | OAuth 2.0 client_id from the BMX partner portal. |
client_secret | string | yes | OAuth 2.0 client_secret. Stored encrypted. |
api_base | string | no | Override the BMX API root. Default https://api.butterflymx.com. Only set if BMX has directed you to a regional or staging endpoint. |
token_url | string | no | Override the OAuth2 token endpoint. Default https://accounts.butterflymx.com/oauth/token. |
building_id | string | no | Building UUID filter when the credentials span multiple properties. Scopes which doors receive live state/event updates; get_doors/get_buildings/get_panels still return everything the credentials can see. |
status_interval | int | no | Door roster poll cadence in milliseconds (default 60000, minimum 15000). |
events_interval | int | no | Access-event feed poll cadence in milliseconds (default 30000). Raise it to reduce cloud calls. The field hint says 0 disables event polling, but a 0 currently falls back to the 30000 ms default rather than stopping the feed — raise the value instead of zeroing it if the goal is fewer calls. |
request_timeout | int | no | Per-request timeout (default 15000 ms). |
Zone attributes
| Name | Type | Description |
|---|---|---|
address | string | The BMX door UUID (required). |
released_state_ms | int | How long zone.state stays "released" after an unlock before clearing back to "idle". Does NOT change the strike duration — that's set on the door in the BMX portal. Default 5000 ms. |
state | string | Maintained by the driver. "idle" normally, "released" for released_state_ms after an unlock. |
last_event | string | Most recent access event type for this door. |
last_event_at | string | ISO timestamp of the most recent access event. |
last_event_user | string | Display name of the actor on the most recent access event, if BMX surfaced one. |
Zone address format
Plain BMX door UUID, e.g. 5f3a1c2e-8b9d-4f7a-b6e2-1a2b3c4d5e6f. Get the list with the get_doors command after first connect.
Commands
| Command | Args | Notes |
|---|---|---|
unlock | address | Release / open the door. Strike duration is set on the BMX side. |
open | address | Alias for unlock. |
get_buildings | — | List buildings visible to the credentials. |
get_doors | — | List doors. Use this to populate zone addresses. |
get_panels | — | List intercom panels (the hardware door stations). |
get_status | — | Force a status tick and return the cached door roster. |
get_events | optional since | Walk the recent access-log feed. |
refresh_token | — | Force a fresh OAuth2 token (debug). |
Known limitations
- Strike duration is not configurable from GEM. ButterflyMX owns that — set it on the BMX side per door.
- No long-held / always-unlocked state. Most BMX doors are momentary; if a door is configured for an extended hold via BMX's own schedule, the driver does not currently surface that as a distinct zone state (it'll just report
idlebetween release events). - Unlock endpoint is partner-tier. ButterflyMX has versioned this path in the past. If
unlockreturns a 404 on a brand-new partner contract, confirm the current path with your ButterflyMX rep — the driver will continue to function for read-only polling regardless. - No SIP audio/video (see "What this integration does NOT cover").
Troubleshooting
token response missing access_token: status=401— the token endpoint rejected the credentials:client_idorclient_secretis wrong, or the partner account doesn't have API scope enabled. Re-mint the credentials in the BMX portal. (Atoken request failed:message instead means the controller couldn't reachaccounts.butterflymx.comat all — a network/DNS/firewall problem, not a credential problem.)GET /v3/doors HTTP 403— credentials are valid but the partner account doesn't have door-read scope. Contact your BMX rep to widen the scope.POST /v3/doors/<id>/open HTTP 404— the BMX unlock endpoint changed shape for your contract tier. Open a ticket with BMX to confirm the current path; read-only polling will keep working in the meantime.… HTTP 429— the cloud is rate-limiting. Increasestatus_intervalandevents_interval.- Zones never transition past
idle— make surezone.addressmatches a UUID from theget_doorsoutput verbatim. Address fields in the BMX API are case-sensitive. - Device shows disconnected after a few hours — token refresh failed. Check that
accounts.butterflymx.comis reachable from the GEM controller; the driver auto-refreshes ~5 minutes before token expiry.
Related Documentation
- Zones — create one zone per door (the driver does not auto-create them)
- Subsystems — the Doors / Gates subsystems a door zone belongs to
- Devices — adding and managing the ButterflyMX device row
- Commands — running
get_doors/unlockand the rest of the command set - Visitors — visitor-access workflows a buzz-in fits into