SDK Reference

Access Control

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.

How it composes

Location ──< Door
         └─< Room

AccessLevel ──< DoorPermission >── Door
                     │
                     └─< AccessTime  (per-day or "every_day" windows)

Group ──< GroupAccessLevel >── AccessLevel     (see Management)
  • A Location is a physical site; it owns Doors and (loosely) Rooms.
  • An 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.
  • A DoorPermission is the join row that grants an AccessLevel access to one Door.
  • An 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).
  • A 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).

AccessLevel

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.

FieldTypeNotes
idstring
namestring?
descriptionstring?
createdAt / updatedAtDatefull data only
ts
interface CreateAccessLevelData {
  name: string;
  description: string;
}

Endpoint: /company/{cID}/accessLevels. Has icon/viewHref (/company/{cID}/accessLevels/{id}).

ts
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.

AccessTime

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.

ts
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.

FieldTypeNotes
idstring
doorPermissionIDstringparent DoorPermission
unitAccessUnit
startTime / endTimestring?unset when the window spans the whole day
createdAt / updatedAtDatefull data only
ts
interface CreateAccessTimeData {
  doorPermissionID: string;
  unit: AccessUnit;
  startTime: string;
  endTime: string;
}
Gotcha:The UI works in 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}).

ts
get accessTime.doorPermission: DoorPermission | undefined  // cached getter, resolves doorPermissionID
get accessTime.UnitName: string                            // AccessUnitToReadable[unit], or "Invalid Unit" if unset

DoorPermission

zensile-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.

FieldTypeNotes
idstring
accessLevelIDstring
doorIDstring
createdAt / updatedAtDatefull data only
ts
interface CreateDoorPermissionData {
  accessLevelID: string;
  doorID: string;
}

Endpoint: /company/{cID}/doorPermissions. Has icon/viewHref (/company/{cID}/doorPermissions/{id}).

ts
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.

Location

zensile-sdk/company/location/location.ts — a physical facility site; parent to Doors and (loosely) Rooms, and the scope for check-in Visits.

FieldTypeNotesLazy?
idstring✓
companyIDstring?✓
namestring?✓
addressLine1string?✓
addressLine2string?full only
phonestring?full only
city / country / state / postalCodestring?full only
createdAt / updatedAtDatefull only
ts
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}).

ts
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[]>>
Gotcha: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.

Door

zensile-sdk/company/location/door.ts — a physical access point at a Location, granted to users via DoorPermission/AccessLevel, or checked in against directly.

FieldTypeNotesLazy?
idstring✓
companyIDstring?✓
namestring✓
locationIDstring✓
createdAt / updatedAtDatefull 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.

ts
interface CreateDoorData {
  locationID: string;
  name: string;
}

Endpoint: /company/{cID}/doors. Has icon/viewHref (/company/{cID}/doors/{id}).

ts
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.

Note: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.

Room

zensile-sdk/company/location/room.ts — a physical room within a Location, e.g. for class capacity/scheduling.

FieldTypeNotesLazy?
idstring✓
namestring✓
locationIDstring✓
companyIDstringfull only
descriptionstring | nullfull only
capacitynumber | nullfull only
createdAt / updatedAtDatefull only
ts
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;
}
ts
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}).

ts
get room.location: Location | undefined   // cached getter, resolves locationID
Gotcha:room.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.