mappers.ts module provides functions that transform Zenoti API response types into the Etienne Intelligence Platform’s domain models. This allows the dashboard to consume Zenoti data through the same interfaces it uses for seed data.
Centers → Locations
mapCenter()
Transform a single Zenoti center into an EIP Location.
Zenoti center object from API
EIP location object with fields:
id— Center IDname— Display name (falls back tonameifdisplay_nameis empty)city— City namestate— State code or namerooms— Count of active roomsproviders— Provider count
mapCenters()
Transform an array of centers, filtering out inactive ones.
Array of Zenoti centers
Array of EIP locations (only active centers)
Services
mapService()
Transform a Zenoti service into an EIP Service.
Zenoti service object
EIP service object with:
id— Service IDname— Service nameprice— Sales priceduration— Duration in minutescategory— EIP category ('Injectable' | 'Facial' | 'Laser' | 'Body')
mapServices()
Transform an array of services, filtering out inactive ones.
Array of Zenoti services
Array of EIP services (only active)
Appointments
mapAppointment()
Transform a Zenoti appointment into an EIP Appointment.
Zenoti appointment object
EIP appointment with:
id— Appointment IDclientName— Full name (first + last)clientId— Guest IDservice— Service nameprovider— Therapist full namelocationId— Center IDdate— ISO date (YYYY-MM-DD)startTime— HH:MM formatendTime— HH:MM formatstatus— EIP status ('confirmed' | 'completed' | 'no_show' | 'cancelled')bookedBy— Booking source ('staff' | 'online' | 'ai')noShowRisk— Always'low'(EIP AI calculates separately)room— Room number (always 1 for now)revenue— Service price
Status Mapping
Zenoti status codes → EIP status strings (mappers.ts:86-103):| Zenoti Status | Code | EIP Status |
|---|---|---|
| New / Booked | 0 | confirmed |
| Confirmed | 1 | confirmed |
| Checked-in | 2 | confirmed |
| Completed | 4 | completed |
| No-show | 10 | no_show |
| Cancelled | -1 | cancelled |
Booking Source Mapping
Zenoti booking source → EIPbookedBy (mappers.ts:110-123):
| Zenoti Source | Code | EIP bookedBy |
|---|---|---|
| Walk-in | 0 | staff |
| Phone | 1 | staff |
| Online | 2 | online |
| App | 3 | online |
| API | 4 | ai |
mapAppointments()
Transform an array of appointments.
Guests → Clients
mapGuest()
Transform a Zenoti guest into an EIP Client.
Zenoti guest object
EIP client with:
id— Guest IDname— Full nameemail— Email addressphone— Mobile, home, or work phone (in that order)preferredLocation— Home center IDtotalVisits— Visit countclv— Customer lifetime valuelastVisit— ISO date of last appointmentjoinDate— Account creation datefavoriteService— Preferred service IDnoShowCount— No-show count
mapGuests()
Transform an array of guests, filtering out inactive ones.
Sales / Collections → DailyMetrics
mapDailySales()
Convert a Zenoti daily sales breakdown row into EIP DailyMetrics.
Daily sales data from Zenoti sales report
Center ID (not included in ZenotiDailySales)
EIP daily metrics object. Fields that Zenoti doesn’t provide natively (like
responseTimeAvg, callsAnswered, aiResolved) are set to 0 — EIP’s AI modules fill these in from the command center channel.mapSalesReport()
Map a full Zenoti sales report into an array of DailyMetrics.
Sales report from
getSalesReport()Array of daily metrics (one per day in the report’s
daily_breakdown)mapCollection()
Map a Zenoti collection (daily revenue summary) into DailyMetrics. Lightweight alternative when the v2 sales report endpoint is unavailable.
Collection object from
listCollections()Daily metrics with revenue and transaction count. Most fields are set to 0.
mapCollections()
Transform an array of collections.
Field Mapping Tables
Center → Location
| Zenoti Field | EIP Field | Transformation |
|---|---|---|
id | id | Direct |
display_name or name | name | Fallback to name |
city | city | Direct |
state.code or state.name | state | Prefer code |
rooms (filtered is_active) | rooms | Count active |
provider_count | providers | Direct |
Service → Service
| Zenoti Field | EIP Field | Transformation |
|---|---|---|
id | id | Direct |
name | name | Direct |
price.sales | price | Direct |
duration | duration | Direct |
category.name | category | Mapped via CATEGORY_MAP |
Appointment → Appointment
| Zenoti Field | EIP Field | Transformation |
|---|---|---|
appointment_id | id | Direct |
guest.first_name + last_name | clientName | Concatenated |
guest.id | clientId | Direct |
service.name | service | Direct |
therapist.first_name + last_name | provider | Concatenated |
center_id | locationId | Direct |
start_time | date, startTime | Parsed to ISO date + HH:MM |
end_time | endTime | Parsed to HH:MM |
status | status | Mapped via mapAppointmentStatus() |
booking_source | bookedBy | Mapped via mapBookingSource() |
price.sales | revenue | Direct |
Guest → Client
| Zenoti Field | EIP Field | Transformation |
|---|---|---|
id | id | Direct |
personal_info.first_name + last_name | name | Concatenated |
personal_info.email | email | Direct |
personal_info.mobile_phone.number | phone | Fallback to home/work |
home_center_id | preferredLocation | Direct |
total_visits | totalVisits | Direct |
clv | clv | Direct |
last_visit_date | lastVisit | Direct |
creation_date | joinDate | Direct |
preferred_service_id | favoriteService | Direct |
no_show_count | noShowCount | Direct |
DailySales → DailyMetrics
| Zenoti Field | EIP Field | Transformation |
|---|---|---|
date | date | Direct |
| — | locationId | From parameter |
revenue | revenue | Direct |
bookings | bookings | Direct |
no_shows | noShows | Direct |
| — | noShowRate | Calculated: (noShows / bookings) * 100 |
utilization_rate | utilizationRate | Converted to percentage |
new_clients | newClients | Direct |
| — | All call/AI metrics | Set to 0 (EIP fills these) |
Usage Patterns
Fetch and Map
Use with React Query Hooks
The React Query hooks automatically apply mappers:Extend Mappers
You can compose custom mappers for specific use cases:Related
API Endpoints
Fetch raw Zenoti data
React Hooks
Pre-mapped queries with caching
TypeScript Types
Full type definitions