AccessLevel, AccessTime, DoorPermission, Location, Door, Room — the KISI-integrated door-access model plus the physical-site hierarchy it hangs off of. See Introduction for DataStructure<T>, SimpleResult<T>/EventData<T>, and QueryRequest<T> conventions shared by every class below.
Location ──< Door
└─< Room
AccessLevel ──< DoorPermission >── Door
│
└─< AccessTime (per-day or "every_day" windows)
Group ──< GroupAccessLevel >── AccessLevel (see Management)Location is a physical site; it owns Doors and (loosely) Rooms.AccessLevelis a named tier (e.g. "Staff", "24-Hour Member") with no doors of its own — it only becomes meaningful once linked to specific doors.DoorPermission is the join row that grants an AccessLevel access to one Door.AccessTime is a scheduling window (a day of week, or "every_day") scoped to one DoorPermission, restricting when that grant is active. No AccessTimerows at all implicitly means unrestricted/always-open for that permission (the UI's AccessTimeWeekView is the editor for this).Group's members inherit access levels via GroupAccessLevel (see Management) — this is how a whole cohort of members gets door access at once instead of assigning DoorPermission per member.All six classes here follow the standard DataStructure<T> shape: id/companyID constructor overloads (new X(id, company) for lazy-preload-by-id, or new X(data, company, lazy?) to hydrate from already-fetched data), a static _cache: Map, fetch(lazy?), save() (PATCH [this._data]), a static create(company, items[]) (POST, returns EventData<string[]> of new ids), delete()/Delete() (DELETE by ?ids=), and a static query(company, data: QueryRequest) (POST to .../query, wraps results back into instances — lazy by default unless data.lazy === false).
zensile-sdk/company/access/accessLevel.ts — a named tier of door access; the grouping that DoorPermission rows attach to, and that a Group can be linked to via GroupAccessLevel.
| Field | Type | Notes |
|---|---|---|
| id | string | |
| name | string? | |
| description | string? | |
| createdAt / updatedAt | Date | full data only |
interface CreateAccessLevelData {
name: string;
description: string;
}Endpoint: /company/{cID}/accessLevels. Has icon/viewHref (/company/{cID}/accessLevels/{id}).
async accessLevel.GetDoorPermissions({ limit = 100, offset = 0, lazy = true }): Promise<SimpleResult<DoorPermission[]>>Convenience wrapper around DoorPermission.query() filtered by accessLevelID — the parent → children pattern used throughout the codebase.
zensile-sdk/company/access/accessTime.ts — a single allowed time window (a specific day, or "every_day") for a DoorPermission. This is what AccessTimeWeekView (drag-to-create weekly grid) creates/edits/deletes.
type AccessUnit = "every_day" | "mon" | "tues" | "wed" | "thurs" | "fri" | "sat" | "sun";AccessUnitToReadable: Record<AccessUnit, string>gives display labels ("Every Day", "Monday", …). AccessTimeWeekView renders "every_day" blocks in a separate dashed overlay lane distinct from day-specific blocks.
| Field | Type | Notes |
|---|---|---|
| id | string | |
| doorPermissionID | string | parent DoorPermission |
| unit | AccessUnit | |
| startTime / endTime | string? | unset when the window spans the whole day |
| createdAt / updatedAt | Date | full data only |
interface CreateAccessTimeData {
doorPermissionID: string;
unit: AccessUnit;
startTime: string;
endTime: string;
}HH:MM; the backend requires HH:MM:SS. Both save() and create() pad any 5-character time string with :00before sending — if you're constructing startTime/endTime yourself, either format matches what these methods produce, but don't assume the value round-trips unchanged.Endpoint: /company/{cID}/accessTimes. Has icon/viewHref (/company/{cID}/accessTimes/{id}).
get accessTime.doorPermission: DoorPermission | undefined // cached getter, resolves doorPermissionID
get accessTime.UnitName: string // AccessUnitToReadable[unit], or "Invalid Unit" if unsetzensile-sdk/company/access/doorPermission.ts — the join row granting one AccessLevel access to one Door. AccessTime windows attach to a specific DoorPermission (not directly to the door or access level), so the same access level can carry different schedules per door.
| Field | Type | Notes |
|---|---|---|
| id | string | |
| accessLevelID | string | |
| doorID | string | |
| createdAt / updatedAt | Date | full data only |
interface CreateDoorPermissionData {
accessLevelID: string;
doorID: string;
}Endpoint: /company/{cID}/doorPermissions. Has icon/viewHref (/company/{cID}/doorPermissions/{id}).
get doorPermission.accessLevel: AccessLevel | undefined // cached getter
get doorPermission.door: Door | undefined // cached getter
async doorPermission.GetListOfAccessTimes({ limit = 100, offset = 0, lazy = true }): Promise<SimpleResult<AccessTime[]>>GetListOfAccessTimes wraps AccessTime.query() filtered by doorPermissionID — this is what backs AccessTimeWeekView's per-permission schedule.
zensile-sdk/company/location/location.ts — a physical facility site; parent to Doors and (loosely) Rooms, and the scope for check-in Visits.
| Field | Type | Notes | Lazy? |
|---|---|---|---|
| id | string | ✓ | |
| companyID | string? | ✓ | |
| name | string? | ✓ | |
| addressLine1 | string? | ✓ | |
| addressLine2 | string? | full only | |
| phone | string? | full only | |
| city / country / state / postalCode | string? | full only | |
| createdAt / updatedAt | Date | full only |
interface CreateLocationData {
name: string;
addressLine1: string;
addressLine2?: string;
phone: string;
city: string;
country: string;
state: string;
postalCode: string;
}Endpoint: /company/{cID}/locations. Has icon/viewHref (/company/{cID}/locations/{id}).
get location.FullAddress: string // formats addressLine1[, addressLine2], city, country postalCode as one string
async location.GetListOfDoors({ limit = 100, offset = 0, lazy = true }): Promise<SimpleResult<Door[]>>
async location.GetListOfRooms({ limit = 100, offset = 0, lazy = true }): Promise<SimpleResult<Room[]>>
async location.GetListOfVisits({ limit = 100, offset = 0, lazy = true }): Promise<SimpleResult<Visit[]>>GetListOfDoors and GetListOfVisits correctly filter by locationID. GetListOfRooms does not — it calls Room.query(this.company, { limit, offset, lazy }) with no locationIDfilter, so it actually returns the company's rooms generally, not just this location's. Filter client-side (or by room.location) if you need rooms scoped strictly to one location.zensile-sdk/company/location/door.ts — a physical access point at a Location, granted to users via DoorPermission/AccessLevel, or checked in against directly.
| Field | Type | Notes | Lazy? |
|---|---|---|---|
| id | string | ✓ | |
| companyID | string? | ✓ | |
| name | string | ✓ | |
| locationID | string | ✓ | |
| createdAt / updatedAt | Date | full only |
No KISI-specific fields (e.g. an external device/lock id) appear on DoorData in this file — the KISI integration is company-level (see ADMIN_INTEGRATIONS and company.ts's Mindbody/KISI wiring, see Company), not a per-door column here.
interface CreateDoorData {
locationID: string;
name: string;
}Endpoint: /company/{cID}/doors. Has icon/viewHref (/company/{cID}/doors/{id}).
get door.location: Location | undefined // cached getter, resolves locationID
async door.GetListOfPermissions({ limit = 100, offset = 0, lazy = true }): Promise<SimpleResult<DoorPermission[]>>
async door.GetListOfVisits({ limit = 100, offset = 0, lazy = true }): Promise<SimpleResult<Visit[]>>
async door.checkIn(id: string) // POST /company/{cID}/checkIn with [{ id, doorID: this.id }]GetListOfPermissions wraps DoorPermission.query() filtered by doorID — the reverse of AccessLevel.GetDoorPermissions(), letting you enumerate which access levels open this specific door.
checkIn(id) is a low-level direct call (id is presumably a member id) — prefer the standalone CreateVisits({ company, visits }) function (see Members) for the standard check-in flow with alert handling, since door.checkIn here returns the raw APIRequest result rather than the CheckInResponse envelope.zensile-sdk/company/location/room.ts — a physical room within a Location, e.g. for class capacity/scheduling.
| Field | Type | Notes | Lazy? |
|---|---|---|---|
| id | string | ✓ | |
| name | string | ✓ | |
| locationID | string | ✓ | |
| companyID | string | full only | |
| description | string | null | full only | |
| capacity | number | null | full only | |
| createdAt / updatedAt | Date | full only |
interface CreateRoomData {
locationID: string;
name: string;
description: string | null;
capacity: number | null;
}
/** Payload shape for updating a room directly (rather than through room.save()). */
interface UpdateRoomData {
id: string;
name: string;
description: string | null;
capacity: number | null;
}static async create(company: Company, rooms: CreateRoomData[]): Promise<EventData<string[]>>CreateRoomData carries locationIDper item — a room's parent location is set per-item in the payload, not via a separate positional argument. UpdateRoomDataexists as a typed shape for a direct-PATCH payload but isn't used by room.save() itself.
Endpoint: /company/{cID}/rooms. Has icon/viewHref (/company/{cID}/rooms/{id}).
get room.location: Location | undefined // cached getter, resolves locationIDroom.save() first calls await this.data() to force-resolve full data (since _data may only hold the lazy shape lacking description/capacity) before PATCHing — unlike most other save() implementations here, which just PATCH this._data as-is. If you've only ever loaded a Room lazily and mutate a field before calling save(), make sure you're mutating the full data object (post-await room.data()), not the lazy one.