Skip to content
← Back to blog
8 min read

The Hidden Engineering Cost of Off-the-Shelf SaaS

We ran a spa on Zenoti for several years at $318/month. The subscription was never the real price — metered add-ons and engineering time were. A post-mortem on SMS credits at 3–4× market, integration debt, and how to evaluate vertical SaaS before you sign.

Post-MortemSaaSPricingIntegrationsEngineering Management

We operated a spa on Zenoti — a vertical SaaS platform for spas, salons, and wellness businesses — for several years, at $318/month. Over that time we opened dozens of support tickets, built custom integrations against their API, and invested a meaningful chunk of engineering time working around platform limitations. Eventually we moved off it.

This isn't a "Zenoti is bad" post. Zenoti is a mature product that plenty of large operators run happily, and much of what follows is a story about a mismatch between a platform's assumptions and ours. It's a post-mortem on a more general failure: we evaluated a SaaS platform on its subscription price and feature checklist, and paid for it everywhere else. That's a mistake I've now watched play out at more than one company, and it's worth writing down.

The $318 was the number we budgeted for. It was not the number we paid.

The core mismatch

Vertical SaaS is priced and designed around a median customer. The further you sit from that median, the more of the gap you backfill yourself — and the gap is invisible during evaluation, because demos are built on the median.

We were unusual in one specific way: we had an engineer. That sounds like an advantage. It was actually the trap. Because we could build around limitations, we did — repeatedly, and each time it felt locally cheaper than switching platforms. A business without in-house engineering would have hit a hard wall in month two and made a decision. We had a soft wall, and we kept paying to climb it.

Where the time actually went

The subscription was a line item we noticed every month. These were the costs we didn't:

  • Opening and shepherding support tickets — not the filing, the follow-up
  • Waiting on engineering investigations while an operational issue stayed live
  • Building manual workarounds and then maintaining them
  • Writing custom software for things we assumed were product features
  • Exporting and reconciling data
  • Troubleshooting integrations we'd built ourselves

Individually, each of these is a normal cost of doing business on someone else's platform. Aggregated over several years, they exceeded the subscription. That's the finding. The subscription wasn't the expensive part; the metered add-ons and the gaps around the software were.

The clearest number: SMS credits at 3–4× market

Most of this post is a judgment call. This part isn't — it's arithmetic.

Zenoti sells SMS in credit packs. The pack we bought:

  • 5,000 SMS credits — $225.00, one-time purchase, valid 180 days

That's $0.045 per SMS. Here's what the same message costs if you send it yourself through Twilio, the infrastructure provider a platform like this sits on top of:

Per SMS5,000 SMS
Zenoti credits$0.045$225.00
Twilio (base rate)$0.0083$41.50
Twilio (incl. carrier fees)~$0.011–$0.015~$54–$77

Twilio's US outbound base rate is $0.0083 per segment, and US carriers add roughly $0.0025–$0.007 on top depending on which network delivers it. I'm using the all-in figure, because it's the fair comparison — the honest markup is about 3–4×, not the 5.4× you'd get by quoting the base rate alone. On the pack we actually bought, that's roughly $150–170 of margin per 5,000 messages.

Some margin here is legitimate. Zenoti is carrying the deliverability, the compliance surface, the 10DLC registration, and the support burden of running messaging for thousands of businesses. That's real work and it should be priced. I'm not arguing SMS should be at cost.

But then there's the 180-day expiry. Unused credits don't roll over — they evaporate. That's a detail worth naming plainly, because it means the effective price is $0.045 only if you burn the whole pack in six months. If you use 3,000 of them, you paid $0.075 each. The expiry converts a usage-based charge into something closer to a subscription with extra steps, and it quietly transfers the forecasting risk to you.

Where the pricing and the rigidity compound

The SMS markup on its own is a line item you can swallow. What made it actually expensive was that we couldn't control what went into the messages.

We send booking notifications to our own service providers — our staff. What those people need to know is short: who's booked, what service, what time. What Zenoti's template sends is that, plus the salon address, plus the cancellation policy, plus instructions on how to cancel.

Every one of those lines is dead weight. Our providers know where they work. They are not going to cancel the appointment as if they were the client. This is boilerplate written for a customer-facing confirmation, being sent to staff, because the template wasn't ours to change.

The problem is that SMS bills by segment, not by message. A GSM-7 message fits 160 characters in one segment; past that it splits, and concatenated segments only carry 153 characters each. An address and a cancellation policy will blow through 160 characters without effort. So a notification that should have been one segment goes out as two or three — and each one bills.

That's the compounding. A rigid template doesn't just annoy you; it multiplies your unit cost against a rate that's already at 3–4× market. Paying $0.045 for a segment you needed is a pricing decision you can accept. Paying $0.090 or $0.135 to tell an employee the address of the building they're standing in is the platform's inflexibility showing up on your invoice, every single booking, forever.

And the fix was unavailable at every level. We couldn't edit the template. We couldn't route provider notifications through our own Twilio account, which would have cost us roughly a fifth as much for a message we'd have controlled completely. The API let us build a great deal — but not this.

This is the whole thesis of the post in one line item: the platform decided what we sent, and then charged us 3–4× market to send it.

Integration surface was ours to build

The clearest example: we ended up building our own synchronization layer for appointments, invoices, payments, tips, providers, packages, guests, and appointment deletions.

That's not a small integration. That's a distributed systems problem — bidirectional sync, conflict resolution, idempotency, reconciliation for the cases where the two sides disagree. Appointment deletion alone is genuinely hard, because a deletion is the one event a polling-based sync can't observe by looking at what exists.

To be fair to Zenoti: they publish a real API and they explicitly encourage customers to build custom functionality when the core product doesn't cover a need. That's an honest and reasonable position, and the flexibility has genuine value. But "the platform has an API" and "the platform does this" are not the same sentence, and during evaluation we heard them as the same sentence. An API is a permission to build, not a feature. It moves work to you.

Support latency compounds differently than you'd expect

Our ticket history has a recognizable shape: repeated follow-ups, tickets moving between teams, long investigations, requests for additional logs, and issues that stayed open for extended periods.

The frustrating part wasn't the raw latency. It was that latency on an operationally-live issue is not linear in cost. If a booking flow is broken, every day it stays broken is a day of manual workaround by staff who should be serving customers — and that workaround itself has to be built, explained, and then unwound later. A ticket open for three weeks isn't three weeks of waiting; it's three weeks of parallel manual process. Instead of fixing problems once, we kept following up.

Configuration complexity is a real, recurring tax

Many workflows required a tour through numerous configuration screens. Finding the right setting meant reading documentation, contacting support, testing configurations, and repeating. This isn't unique to us — it's a common complaint about deeply configurable vertical platforms, and it's arguably the direct cost of the flexibility that makes them viable for large operators in the first place.

The tradeoff is legible once you name it: configurability is a transfer of complexity from the vendor to you. Enterprises absorb that transfer with an ops team. We absorbed it with my evenings.

Owning your data is not the same as accessing it

As an owner, I wanted our data to be ours. In practice we regularly needed support involvement for exports, reporting, payment information, gift card data, and API questions.

API access helps, and I don't want to undersell it — a platform with no API is far worse. But extracting business data in a usable shape still took real technical effort. There's a meaningful distance between "your data is accessible" and "your data is usable without an engineer," and that distance is where a lot of switching cost quietly accumulates. The day you want to leave is the day you find out how far apart those two things are.

What I'd do differently

The lesson isn't "avoid Zenoti," and it definitely isn't "build it yourself" — building a spa platform from scratch would have been vastly worse. It's that the evaluation was wrong. If I were choosing a vertical SaaS platform again, I'd price these explicitly before signing:

  1. Estimate the integration surface up front. List every entity you'll need to sync and ask, concretely, which ones you'd be building yourself. Treat every answer of "you can do that via the API" as a line item with an engineering estimate attached, not as a yes.
  2. Ask about support latency on business-critical paths specifically. Not average response time — resolution time on issues that block daily operations, and what the escalation path actually looks like.
  3. Test the exit on day one. Try to export your data before you depend on the platform, not after. If you need support's help to get your data out during evaluation, you'll need it under worse conditions later.
  4. Price the metered add-ons against the underlying provider. Take every usage-based charge — SMS, email, storage, payments — and divide it by what Twilio, SES, or Stripe would charge you directly. A 3–4× markup on a channel you're forced to use is a permanent tax, and it's the easiest thing to check before signing and the hardest to escape after. Ask what expires, too.
  5. Check whether you control the content of what you're metered on. A markup you can't avoid is bad. A markup on messages whose length someone else decides is worse, because the two multiply.
  6. Price the total cost of ownership, not the subscription. Developer hours, staff training, support meetings, troubleshooting, and the projects that don't happen because you're doing the above. For us this dominated, and it never appeared on an invoice.
  7. Be honest about whether you're the median customer. If you're not, you're paying the difference in labor.

Would I recommend Zenoti?

Genuinely, it depends, and I think the split is fairly clean.

For large enterprises with dedicated IT teams and developers, the extensive customization is a real asset. You have the staff to absorb the complexity transfer, and in exchange you get a platform that bends to your process rather than the other way around. That's a good trade.

For small and medium spas, med spas, salons, and wellness businesses without in-house engineering resources, our experience was that the operational overhead outweighed the benefits. Not because the software is bad, but because the model assumes an engineering capacity you may not have — and if you do have it, you'll spend it here instead of on your business.

We wanted software that simply worked, so we could spend our time serving customers, marketing, and growing the business — not opening support tickets. That's ultimately why we left.


If you're weighing the same decision, I wrote up both halves of what comes next:

  • How to export your data out of Zenoti → — the auth problem, every endpoint and its limits, and the five bugs that cost us duplicate guests, duplicate invoices, and a calendar full of $0 totals.
  • Build a Zenoti clone → — a complete implementation prompt for the platform you'd move to, including the rules that keep a sync from destroying your billing history.