Contact-centre analytics Β· UK holiday lettings
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.
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
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.
src/config.py.
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.
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
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
Contact volume by day of week and hour
All drivers, all channels. Darker is busier.
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.
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
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
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.
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.
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
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
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.
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
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.
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
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.
What moves the answer
3-year NPV as each input is varied alone; effect size uses the measured 95% CI
NPV across deflection rate and SMS unit price
Red cells lose money. The measured effect and list price are marked.
What has to be true β the part most business cases leave out
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
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
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
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 model | Service Cloud |
|---|---|
contact_id | Case.CaseNumber |
driver | Case.Reason |
channel | Case.Origin |
agent_id | Case.OwnerId |
booking_id | Case.Booking__c |
first_contact_resolution | Case.FCR__c |
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.