Skip to content

Models

All response models are Pydantic v2 BaseModel subclasses. The API returns camelCase field names; each model uses alias_generator = to_camel so you can access fields with Pythonic snake_case attributes.

customer = client.customers.get("12345")
print(customer.first_name)   # snake_case attribute
print(customer.model_dump(by_alias=True))  # camelCase dict

Customers

CustomerListItem

Bases: SonnysModel

Summary customer record returned by client.customers.list().

Contains identifiers, name, phone, and active status.

Customer

Bases: SonnysModel

Full customer profile returned by client.customers.get(id).

Includes address, contact details, loyalty info, and SMS opt-in status.


Items

Item

Bases: SonnysModel

A menu item (wash package, product, or service) returned by client.items.list().

Includes SKU, pricing, and department info.


Employees

EmployeeListItem

Bases: SonnysModel

Summary employee record returned by client.employees.list().

Contains name and employee ID.

Employee

Bases: SonnysModel

Full employee profile returned by client.employees.get(id).

Includes contact info, active status, and start date.

ClockEntry

Bases: SonnysModel

A single clock-in/clock-out record for an employee.

Returned by client.employees.get_clock_entries().


Sites

Site

Bases: SonnysModel

A car wash site/location returned by client.sites.list().

Contains the site code, name, and timezone.


Giftcards

GiftcardListItem

Bases: SonnysModel

A giftcard liability record returned by client.giftcards.list().

Contains card number, value, amount used, and site code.


Washbooks

WashbookListItem

Bases: SonnysModel

Summary washbook record returned by client.washbooks.list().

Contains ID, balance, status, and sign-up date.

Washbook

Bases: SonnysModel

Full washbook detail returned by client.washbooks.get(id).

Includes customer, vehicles, tags, and recurring billing info.

WashbookTag

Bases: SonnysModel

An RFID tag or barcode associated with a washbook account.

WashbookVehicle

Bases: SonnysModel

A vehicle linked to a washbook account.


Recurring

RecurringListItem

Bases: SonnysModel

Summary recurring account returned by client.recurring.list().

Contains ID, plan name, status, balance, and billing site.

Recurring

Bases: SonnysModel

Full recurring account detail returned by client.recurring.get(id).

Includes customer, vehicles, tags, billing history, and status history.

RecurringStatusChange

Bases: SonnysModel

A status change event returned by client.recurring.list_status_changes().

Records old/new status, date, employee, and site.

RecurringModification

Bases: Recurring

A recurring account with its modification history.

Extends :class:Recurring with a modifications list. Returned by client.recurring.list_modifications().


Transactions

TransactionListItem

Bases: SonnysModel

Summary transaction record returned by client.transactions.list() and client.transactions.list_by_type().

Contains transaction number, ID, total, and date.

TransactionV2ListItem

Bases: TransactionListItem

Enriched transaction summary returned by client.transactions.list_v2().

Extends :class:TransactionListItem with customer ID, recurring plan flags, and transaction status.

Transaction

Bases: SonnysModel

Full transaction detail returned by client.transactions.get(id).

Includes line items, tenders, discounts, customer/employee info, and prepaid/recurring flags.

TransactionJobItem

Bases: Transaction

Transaction record returned by client.transactions.load_job().

Extends :class:Transaction with additional fields from the batch job endpoint.

TransactionTender

Bases: SonnysModel

A payment tender (cash, credit, etc.) within a transaction detail.

Contains tender type, amount, change, and optional credit card info.

TransactionItem

Bases: SonnysModel

A line item within a transaction detail.

Contains the item name, SKU, department, quantity, and price breakdown (gross, net, discount, tax).

TransactionDiscount

Bases: SonnysModel

A discount applied within a transaction detail.

Records the discount name, code, amount, and which item it was applied to.


Stats

SalesResult

Bases: SonnysModel

Revenue breakdown returned by client.stats.total_sales().

Categorizes total revenue into three buckets based on transaction flags: recurring plan sales, recurring redemptions, and retail. Each bucket includes both a revenue total and a transaction count.

WashResult

Bases: SonnysModel

Wash volume breakdown returned by client.stats.total_washes().

Categorizes wash transactions using v2 flags, v1 type=wash, and v1 type=recurring:

  • member_wash_count: Transactions where is_recurring_plan_redemption is True (membership washes).
  • retail_wash_count: Non-member car washes — type=wash transactions (excluding plan sales and redemptions) plus unknown non-negative transaction types.
  • free_wash_count: Washes with total == 0.
  • eligible_wash_count: Derived as total - member_wash_count - free_wash_count. Used as the denominator in conversion rate calculations. Includes plan sale washes and positive-total unknown types.

The total field is member + retail + plan_sale_washes. Negative-total transactions (refunds) are excluded entirely.

ConversionResult

Bases: SonnysModel

Membership conversion KPI returned by client.stats.conversion_rate().

Measures how effectively a site converts eligible wash customers into membership sign-ups. The rate is computed as new_memberships / eligible_washes.

A rate of 0.15 means 15 % of eligible washes resulted in a new membership sale. When there are zero eligible washes the rate is 0.0 (division-by-zero safe).

LaborCostResult

Bases: SonnysModel

Labor cost breakdown returned by client.stats.total_labor_cost().

Aggregates pay data from all clock entries in the requested period, splitting costs into regular and overtime buckets. Each cost is computed as rate * hours for the corresponding pay type, then summed across all entries.

Attributes:

Name Type Description
total_cost float

Combined regular and overtime cost (regular_cost + overtime_cost).

regular_cost float

Sum of regular_rate * regular_hours across all clock entries.

overtime_cost float

Sum of overtime_rate * overtime_hours across all clock entries.

regular_hours float

Total regular hours worked across all entries.

overtime_hours float

Total overtime hours worked across all entries.

total_hours float

Combined hours (regular_hours + overtime_hours).

entry_count int

Number of clock entries aggregated.

CostPerCarResult

Bases: SonnysModel

Cost-per-car KPI returned by client.stats.cost_per_car().

Measures labor efficiency by dividing total labor cost by the number of washes in the period. A value of 4.25 means the site spent $4.25 in labor for each car washed.

When there are zero washes the cost_per_car is 0.0 (division-by-zero safe).

Attributes:

Name Type Description
cost_per_car float

Total labor cost divided by total washes (0.0 when total_washes is zero).

total_labor_cost float

Aggregate labor cost from :class:LaborCostResult.

total_washes int

Wash count from :class:WashResult.

StatsReport

Bases: SonnysModel

Unified analytics report returned by client.stats.report().

Bundles all KPIs into a single result object, computed from a single v2 transaction fetch for efficiency.

Attributes:

Name Type Description
sales SalesResult

Revenue breakdown (recurring plan sales, recurring redemptions, and retail) as a :class:SalesResult.

washes WashResult

Wash volume breakdown (retail, member, and eligible washes) as a :class:WashResult.

new_memberships int

Count of recurring plan sales during the report period.

conversion ConversionResult

Membership conversion rate KPI as a :class:ConversionResult.

labor LaborCostResult

Labor cost breakdown (regular/overtime costs and hours) as a :class:LaborCostResult.

cost_per_car CostPerCarResult

Labor cost per car KPI as a :class:CostPerCarResult.

period_start str

ISO-8601 date string for the start of the report range (e.g. "2026-01-01").

period_end str

ISO-8601 date string for the end of the report range (e.g. "2026-01-31").


BackOffice

TimesheetShift

Bases: SonnysModel

A single punch-in/punch-out shift for one employee.

One BackOffice detail row = one shift. Employees can have multiple shifts per day, potentially at different sites.

When an employee is still clocked in at the moment the report was rendered (i.e. the shift is in progress), date_out and time_out are None. Use :attr:is_open to test for this cleanly. BackOffice still reports regular_hours, regular_wages, and total_wages for open shifts based on elapsed time up to the moment the page was rendered.

is_open property

is_open: bool

True when the shift has no clock-out yet (employee is still on the clock at the moment the report was rendered).

EmployeeTimesheet

Bases: SonnysModel

All shifts for one employee in the reporting period, plus their per-employee rollup totals from the "Total for :" row.

BackOfficeTimeclockResult

Bases: SonnysModel

Complete employee-timesheets / timeclock report for a date range.

Returned by :meth:BackOfficeResource.timeclock. Contains per-shift detail nested under per-employee rollups, plus grand totals parsed from the final "Timesheet Total:" footer row.