Operational Insight / Westerly Cottages

Contact-centre analytics Β· UK holiday lettings

One text message removed a third of the calls about where the key is

A full analytical stack for a fictional 3,600-property holiday-lettings business: contact driver analysis, a demand forecast built on the forward booking book, a causal measurement of that text message β€” which the obvious method gets wrong by a third β€” and the costed case for rolling it out.

Contacts analysed
18 months, voice, email and chat
Annual handling cost
at per contact
Forecast error
β†’
vs the method the centre uses today
Net benefit identified
a year, across two costed changes

Read this first: the data is synthetic.

Westerly Cottages does not exist. Every property, booking, contact, advisor and survey response in this project was generated by src/generate_data.py from a seeded simulation. No real customer, owner or employee data was used, and none of it is derived from any real company's systems.

The simulation is where the effort went. Arrivals concentrate on Friday and Saturday changeovers; August saturates and demand spills; the January booking surge is emergent rather than painted on; three named storms drive regional cancellation and fault spikes. The analysis that follows is real analysis applied to a business that behaves like the real thing β€” and every method here would run unchanged against Salesforce Service Cloud data. How it is built β†’

Section 1

The volume list and the cost list are not the same list

Ranking contact drivers by how often they happen is the default report, and it points at the wrong things. Handling cost is volume times handle time, and handle time varies by a factor of three across drivers. Only one of these two rankings is a budget.

Annual handling cost by contact driver

Volume rank shown against each bar. Arrival information is highlighted: it is the largest driver that should not exist at all.

Cost is handling minutes (talk plus after-call work) at the fully loaded advisor rate of per productive minute β€” salary plus on-costs plus overhead, divided by productive hours after 30% shrinkage. The full derivation is in src/config.py.

What is actually addressable

Every driver is classified once, in config, as avoidable, partly avoidable or value-add β€” and the classification is argued in the file rather than assumed in a spreadsheet. Cancellation calls are not waste. A guest cancelling a Β£900 week wants a human, and removing that contact removes the chance to save the booking.

Avoidable contacts run at of volume and a year. Adding a stated of the partly-avoidable drivers gives an addressable pool of a year. That share is a judgement, so it is declared and applied in the open, never folded silently into a headline.

Repeat contact is the quiet one

A repeat contact β€” same guest, same booking, within seven days β€” is avoidable whatever it is about, because the chance to resolve it had already been taken and missed. There are of them in the window, costing a year.

They cluster where first-contact resolution is worst. In-stay property faults resolve first time only of the time, because the fix needs an owner, a housekeeper or a hot-tub engineer β€” and the guest rings back to find out what happened.

Section 2

The single biggest avoidable driver, and exactly when it lands

Arrival information and key collection is the second largest driver by volume and costs a year to handle. It is not spread evenly: it is a wall that hits in the last 48 hours before the guest travels.

When arrival-information contacts arrive, relative to the guest's arrival day

Contacts by days before arrival, whole 18-month window

of these contacts land within 48 hours of arrival, and the single biggest day is the day before travel. That is a guest who has been holding a booking for weeks and still does not know where to collect the key.

Contact volume by day of week and hour

All drivers, all channels. Darker is busier.

The centre's two busiest bands are Friday and Saturday afternoons β€” the changeover days. That is not a coincidence: of arrivals are Friday or Saturday, and arrival-driven contact follows them. The deflection therefore lands exactly where the roster is tightest, which is worth more than the average-minute arithmetic suggests.

Which bookings generate them

Root cause, not symptom: the rate rises steadily with lead time β€” from 29 contacts per 100 arrivals on last-minute bookings to 42 on those booked six months or more ahead. A guest who booked eleven months back has forgotten everything in the confirmation email they opened once, in January.

So the fix is obvious

Send the arrival information again, close to arrival, in a channel people actually read. That is not an insight anyone should be paid for β€” it is what the data plainly says.

The analytical work is proving it worked, and by how much. Westerly piloted exactly this on 58% of properties from . Sections 5 and 6 are about measuring it honestly, which turns out to be where the naive answer is wrong by a third.

Section 3

Forecasting contacts from the booking book

Contact demand is driven by arrivals, and arrivals are partly already known: they are sitting on the books. A forecast that ignores the forward booking position is throwing away the best regressor it has.

The plan is built once, at a single origin, and scored across the following twelve weeks. No method sees an actual from the holdout, because a capacity plan cannot either. The benchmark is the method the centre genuinely uses β€” a four-week rolling same-weekday average β€” not a straw man.

12-week holdout: actual daily contacts against three plans

Plan built at the forecast origin and held; shaded band is the model's 10th–90th percentile

The incumbent method flattens the whole seasonal ramp into the last four weeks it happened to see, so it under-plans the run-in to summer and mis-shapes every week.

Accuracy on the holdout

WAPE is used as the headline because it is volume-weighted: being 40% out on a quiet Tuesday matters less than being 8% out on an August Friday, and MAPE cannot tell the difference.

How the specification was chosen

Choosing a model by looking at holdout error is how a 12% MAPE on a slide becomes a 20% MAPE in production. The structural form was picked on a separate validation window inside the training data, before the holdout was scored once.

The forecast had a 10% upward bias until it was told the business had changed

The pre-arrival SMS and a redesigned confirmation email both landed in February 2026 and permanently removed demand. A model fitted across the whole history keeps forecasting the old business: WAPE , bias . Adding a level-shift term at go-live takes it to WAPE. That term could not be selected on the validation window β€” that window's own training data ends before go-live β€” so it is applied because the rollout is a known fact, and its effect is reported separately rather than buried. Measuring change and forecasting demand are the same job; a team that does one without telling the other will over-staff for a year.

Backtested at three origins, including the one where it fails

One twelve-week window is one draw, and the incumbent fails hardest at the turn of a season, so a single spring holdout flatters the model. Replaying the whole pipeline at three origins is the number the business case is allowed to use.

The third window is reported, not deleted

The input is better than the model

Arrivals over the holdout were projected to within WAPE, with a bias of β€” because most of them were already booked. On-the-books arrivals are grossed up by 1 / P(lead time β‰₯ horizon), estimated per arrival month and then backtested at four earlier origins to correct the drift.

Getting that right required fixing a subtle bug that is easy to ship and hard to see: the lead-time distribution must be learned only from arrival dates that have already completed. Including arrival dates still filling up censors the sample towards long lead times, overstates the on-the-books share, and quietly makes every long-horizon forecast run light. Before the fix, arrivals came in 7% under at 12 weeks and 13% under at the far end of the horizon.

Section 4

From a volume line to a number of people

A forecast that stops at a chart has not finished. Daily volume goes through an intraday profile to hourly contact rates, through Erlang C to an agent requirement per hour, and only then through shrinkage to the contracted FTE somebody has to actually recruit.

Weekly FTE requirement against what each plan would have resourced

Holdout period. "Required" is the requirement computed from actual volumes with hindsight.

Mean absolute gap: FTE on the model plan against FTE on the incumbent. Over-staffing avoided across the backtested windows is .

Assumptions on the face of it

Where Erlang C is wrong

It assumes Poisson arrivals, exponential handle times, infinite patience and no abandonment. Two of those are false here: guests do abandon (voice abandon rate abandon rate), and arrivals within an hour are burstier than Poisson.

Net effect: it over-states requirement where abandonment is high and under-states it where arrivals clump. Both plans are scored on the same model, so the comparison holds β€” but the absolute FTE numbers carry that caveat, and a WFM team would re-derive them at 30-minute granularity against real telephony data.

The model plan also ran under-staffed weeks against the incumbent's . A tighter plan carries more service risk. That is a real trade and it belongs in the decision, not in a footnote.

Section 5

Did the pre-arrival SMS work? The obvious answer is wrong by a third

Compare arrival-info contacts before and after go-live on the pilot properties and you get . Report that number and you have just credited the SMS with somebody else's work.

The booking confirmation email was redesigned in the same fortnight. It went to everyone β€” pilot and control alike β€” and it removed demand on its own. The pilot group's before/after change contains both effects, and there is no way to separate them from that group alone.

Arrival-information contacts per 100 arrivals, by arrival week

Pilot properties against non-pilot control. The control group is what tells you how much of the drop was not the SMS.

Both lines fall at go-live. The control group's fall is the confirmation email; the gap that opens between them is the SMS. Difference-in-differences measures that gap.

Four readings of the same event

Checks run before the estimate was believed

  • Parallel trends. Over pre-period weeks the gap between the groups trended per week (p = ) β€” . Without this the design proves nothing.
  • Placebo driver. The same estimator run on payment failures, which an SMS about key collection cannot possibly affect: (p = ). A significant effect here would have meant the design was broken.
  • Pilot balance. Assignment was stratified by region; the two groups match on lead time, party size, nights, grade, hot-tub share and booking value.
  • Ramp excluded. The three-week phased switch-on is dropped from both periods rather than diluted into the post period.
  • Autocorrelation. Weekly operational series are serially correlated; standard errors are heteroskedasticity-robust, and Newey–West for the time-series specifications. Ignoring that makes every effect look more significant than it is.

The number that goes in the business case

A reduction in arrival-information contacts, 95% CI , p < 0.001. The other belongs to the confirmation email β€” which is worth knowing in its own right, and worth crediting to the team that did it.

Section 6

What it is worth, and what has to be true

Two appraisals, both built on measured effects rather than assumed ones. Every input is visible, so a finance business partner can argue with an assumption instead of with the conclusion.

Case A β€” roll the pre-arrival SMS out to the whole portfolio

Contacts avoided
a year at full rollout
Net benefit
a year, after SMS and platform cost
3-year NPV
at 8%, payback months
Breakeven deflection
measured effect is

What moves the answer

3-year NPV as each input is varied alone; effect size uses the measured 95% CI

The case does not hinge on the thing that was measured most carefully. It hinges on average handle time and the price of an SMS β€” both of which are cheap to nail down before committing.

NPV across deflection rate and SMS unit price

Red cells lose money. The measured effect and list price are marked.

Even at the bottom of the confidence interval and the worst SMS price quoted, the rollout stays positive. That is the sentence a sponsor needs, and it is only available because the effect has an interval attached.

What has to be true β€” the part most business cases leave out

    Case B β€” replace the four-week average with the booking-book forecast

    Over-staffing avoided
    a year, annualised from backtested weeks
    Net benefit
    after of analyst time a year
    3-year NPV
    payback months
    Build cost
    15 analyst days at a loaded rate

    What has to be true

      Together

      a year of net benefit, of three-year NPV, for of build. That captures of the addressable pool identified in Section 1 β€” which is the honest framing. The first two changes are the easy ones; the rest of that pool needs work on in-stay faults and amendments, and neither is a one-quarter job.

      Section 7

      The same conclusion, for the people who answer the phone

      An insight that only a data team can read has not landed. This is the whole project on one screen, with no statistics in it, for a team leader with four minutes before a huddle.

      Pre-arrival texts: what we found

      • Roughly one in three guests used to ring us in the last two days before they travelled, asking where the key was. Not because anything went wrong β€” because they had lost the email from months earlier.
      • We started texting them the directions and the key code two days before arrival, for a bit over half our cottages. Those calls dropped by about a third compared with the cottages we did not text.
      • That is around fewer calls a year once every cottage is covered β€” and they were landing on Friday and Saturday afternoons, our hardest hours.
      • Guests were slightly happier too, by of a point on our satisfaction score for arrival questions.
      • It pays for itself in about a year, and it keeps paying after that.

      What we are asking for: texts switched on for the remaining cottages, and a check back in six months to make sure the calls have not crept back.

      Section 8

      How it is built

      The stack

      • Generation β€” seeded NumPy simulation, ~ bookings placed first-come-first-served against a real occupancy calendar, so August saturates rather than growing without limit.
      • Warehouse β€” DuckDB. Staging views over each source, one contact-grain fact table, a handful of aggregates. Five sources become one model; nothing downstream re-joins raw tables.
      • Transformation β€” SQL, deliberately visible rather than hidden inside pandas, with referential-integrity assertions that fail the build rather than reporting a quiet lie.
      • Analysis β€” Python and NumPy. Erlang B/C, log-linear regression with Fourier seasonality, weighted difference-in-differences, segmented regression with Newey–West errors, NPV with two-way sensitivity.
      • Presentation β€” this page (hand-built SVG, no chart library), plus flat extracts sized for Tableau Public and Power BI.

      It would port to Salesforce

      The contact table is modelled on Service Cloud's Case object so the whole warehouse would move with a rename rather than a rewrite:

      This modelService Cloud
      contact_idCase.CaseNumber
      driverCase.Reason
      channelCase.Origin
      agent_idCase.OwnerId
      booking_idCase.Booking__c
      first_contact_resolutionCase.FCR__c

      Data protection

      No real personal data exists in this project, so there is nothing to protect β€” but the design still assumes there would be. Guest and owner identifiers are opaque surrogates, no free-text contact notes are carried into the warehouse, and every analysis runs on aggregates rather than individuals. Against real data the same model would need pseudonymisation at ingest, a retention rule on the contact fact table, and role-based access on anything at guest grain.

      Limitations, stated plainly