All case studies

Food logging by text message, for a nutritionist's private clients

An independent nutritionist wanted her clients to log meals by SMS and get an instant daily total against a limit she sets for each one. We built the tracker, the escalation workflow behind it, and the booking and subscription layer that followed.

Independent nutrition practice (private clients, US)

The brief

An independent nutritionist in the US had a specific idea of how she wanted her clients to log food: no app to install, no site to log into. Send a text saying what you ate, get back the total for the day and how much is left of your personal limit. Nothing off the shelf worked that way — the consumer calorie apps assume an app, a signup and a public catalogue, and none of them let a practitioner control who has access or what each person's limit is.

The requirements that came with it were just as specific. Access had to be invitation-only, with a calorie limit she sets per client. The system had to follow US SMS consent rules (STOP, START, HELP). And it had to read food written in plain language — "150 g of chicken breast", "two scrambled eggs" — without ever inventing a number. When it wasn't sure, she wanted to hear about it, not have the client receive a confident guess.

There was also a practical constraint that shaped the build. Every account — messaging, hosting, APIs, payments — had to be in her name from day one, and the monthly cost had to be low and predictable. This is a one-person practice, not a company with an IT budget.

The scope grew from real use. Once clients were texting daily, she designed features herself as she needed them: teaching the system a nutrition label over SMS, loading a recipe, logging by calories per gram, adding to whatever was logged last. Later came a site with paid consultation bookings, a client portal for weight and measurements, and turning the tracker itself into a monthly plan.

How we built it

  1. Answer in milliseconds, think afterwards

    An inbound text gets an acknowledgement before any work starts — measured in production at 128 ms — and the actual processing finishes around ten seconds later. The hard part of a conversational logger is not speed, it's not counting the same meal twice: the pipeline checks three independent duplicate signals (same message id, same text within 15 seconds, text already received and not yet answered) before anything reaches the log.

  2. The model chooses, it doesn't calculate

    Calories never come from the language model. The system pulls candidate foods from an official nutrition dataset held locally — about 8,000 items — hands the client's text and that candidate list to the model, and gets back a structured answer: the index of the item it picked. If nothing matches, it rephrases the query at most twice, or breaks a meal into up to five items. If it still can't resolve it, the request is escalated to the nutritionist's panel and the client gets a "we'll get back to you shortly". Per-piece weights — one egg, one banana — come from a curated table, not from the model. Anything estimated is flagged with a '~' and stored with the original text so she can review it.

  3. Testing against the real model, not against last week's answer

    Prompt changes are the easiest way to silently break a system like this, so nothing gets touched without running an evaluation suite against the live model. A case only passes if every run lands inside a gram range derived from the physical reality of the food — not from whatever the model returned the previous time. That distinction is what keeps the suite honest as prompts evolve.

  4. Built to run where she already was

    The stack was chosen around her constraints, not ours: PHP without a framework, on shared hosting, with every account in her name and the monthly cost kept deliberately small. Costs are capped by design — a small, cheap model, one SMS segment per reply, and a monthly spend ceiling that shuts messaging off before it overruns.

  5. From tracker to practice: bookings, portal, subscription

    Consultations are booked through a calendar and paid online — reserve first, pay after, with unpaid holds expiring at 30 minutes, and the amount always taken from the payment provider's signed event rather than the URL. The tracker became a monthly subscription with a single place in the code that decides whether a client has access, protected by an architecture test so it can't be bypassed, plus three days of grace, cancellation at period end when someone replies STOP, and a daily reconciliation against the payment provider.

Results

In production since 14 July 2026

The tracker went live within weeks of starting, with paid plans and bookings switched on from 19 August 2026.

12 clients logging by SMS

As of October 2026, twelve of her private clients record their meals by text message, with no app and no login.

128 ms acknowledgement

Every inbound message is acknowledged immediately, and three independent duplicate checks keep a meal from being counted twice.

1,317 automated tests, 3,871 assertions

Green as of 14 October 2026, which is what makes continuous changes to prompts, pricing and features safe to ship.

The system is designed never to invent a figure. When it can't resolve what someone ate, it escalates to the nutritionist's queue instead of guessing.

When the hosting provider's anti-bot layer blocked inbound webhooks for two days, we added a reconciler that recovers missed messages, answers them and emails a report. It now runs as a scheduled job.

Stack

  • PHP 8.2
  • MySQL 8.4
  • Claude Haiku 4.5 (Claude API, structured outputs)
  • USDA FoodData Central (local dataset)
  • Open Food Facts
  • SMS messaging API with webhooks
  • Stripe (Checkout, subscriptions, customer portal)
  • Calendly
  • PHPUnit

Need a tool like this?

Tell us what you need and we'll tell you how we'd approach it. We reply within one business day.

Start a project