Get in touch

From the workshop

The Saucery

Family meal planner that thinks ahead.

Paste any recipe (a link, a screenshot, even a photo of a cookbook page) and it lands as a clean, structured recipe. Meal plans and shopping lists the whole family shares, priced against the supermarket you actually walk into. Native iOS and Android on a Laravel backend.

Live Web iOS Android
Laravel SwiftUI Kotlin Recipe import Agentic assistant Grocery pricing
The build story

The build story

Dinner, debugged.

Every family runs the same nightly standup: what’s for dinner? Meal planning is a coordination problem. Recipes live in screenshots and saved links, the shopping list lives in someone’s head, and the plan lives nowhere. The Saucery is my second run at it. The first was Flutter, and it taught me what the product needed to be before I shelved it. This one is a clean Laravel API with two fully native clients, SwiftUI on iOS and Jetpack Compose on Android, because a kitchen app lives or dies on feeling fast in one hand while the other one stirs.

Paste literally anything

The make-or-break feature is import. If adding a recipe is harder than screenshotting it, nobody migrates. So the importer reads whatever you hand it: a link, a TikTok through the Share sheet, a photo of a cookbook page, the words off a card. Each takes its own road, and every road ends in the same place. Try one.

Imported · from bbcgoodfood.com

Chorizo & Butterbean Stew

One pot, twenty-five minutes, and it tastes like it took all afternoon.

serves 4 10 min prep 25 min cook Spanish one pot

  • 250 g chorizo
  • 800 g butter beans
  • 400 g tinned tomatoes
  • 1 onion
  • 3 cloves garlic
  • 2 tsp smoked paprika
  • 150 g spinach
  • 300 ml chicken stock
  1. Render the chorizo in a dry pan until the fat runs orange, then lift it out.
  2. Soften the onion and garlic in that fat, then the paprika for thirty seconds.
  3. Tomatoes and stock in, simmer ten minutes, then the beans and the chorizo back.
  4. Spinach through at the end, off the heat, and check the salt.
The recipe lands here.
Four roads, one recipe. The pipeline does not care about markup, only about ending with ingredients and steps, and imports run on a stronger model than chat because extraction is where accuracy pays for itself.

An assistant with its hands on the tools

Whisp is the in-app assistant, and it is agentic rather than chatty: nineteen registered tools covering recipe search, the meal plan, the shopping list, cooking history, pantry estimation, the help centre, even grocery price lookups. Ask “plan three dinners the kids will actually eat and put the missing bits on the list” and it operates the same API the apps use. Hands on the tools means a short lead. Anything a stranger wrote, such as a page it imported, reaches the model fenced off as data that is never an instruction. A change that reaches wide (clearing a week, removing staples, editing many recipes at once) is parked until you say yes in your next message. The economics are engineered rather than hoped for: a fast model for conversation, prompt caching on by default, fixed credit costs per action, daily and monthly limits per family, and every call logged with token pricing attached, so the free tier cannot quietly become a fire.

The meter was wrong in both directions

In September I checked the cost log against OpenAI’s invoice, day by day. Over the 40 days where the two counted the same requests, 477 pictures, OpenAI billed US$6.89 and my log said US$15.57. It priced a picture at a flat rate, and OpenAI has no price per picture, only per token. It was wrong the other way as well. A reply the phone walked away from halfway through had been paid for and never logged, because the logging only ran once the stream finished.

Both are fixed, and the real numbers turned up a pricing mistake too. An import was charged about thirteen credits for about one US cent of work: the dearest thing in the app for a family, and one of the cheapest for me. It costs two now, so a new household can load in its own recipes in a weekend.

The week, priced at the shop you actually walk into

A meal plan that cannot tell you what it costs is half a product. So the shopping list prices itself against a real supermarket, Woolworths, Pak’nSave or New World, and then the specific store, because the last two set prices branch by branch and a New World in one suburb can be dearer than the one two suburbs over. The interesting part is the honesty. The app keeps its own price history, so when something is flagged as a deal it can say whether it is one, or whether that is just what the thing costs. Pak’nSave does not publish a was-price at all, so instead of inventing a saving it shows the multibuys and says why. New Zealand only, for now.

The Saucery price details for a block of butter: on special at $5.99, a six-month price chart with a dashed average line, and the verdict that this is a better-than-average special and the lowest in three months.

Six months of one block of butter. The verdict is the app’s, from its own history, not the shelf label’s.

Ask Whisp, in the app. Told that Ruby will not eat the spinach, Whisp answers on a ruled note that she can pick it out, then puts chorizo and butter beans on the list because everything else is in the pantry.

Whisp answers on ruled note paper, one thought to a note, and never opens by announcing what it is.

One source of truth, synced live

Everything is family-scoped on the server (recipes, plans, staples, the list) with roles and invite codes, and the clients are deliberately thin. WebSockets push every change, so when one person ticks off the tomatoes in aisle four, they vanish from the other phone in aisle seven. The backend carries over 1,800 tests, which is what lets two app stores’ worth of releases ship weekly without fear.

Entitlement is a question, not a column

A paid tier that sells on the web, in the App Store and on Google Play has three sources of truth for one yes-or-no question, and they disagree. So the billing layer never stores access. No webhook is allowed to set a flag. It writes down what the store said, and the next read works the answer out again from the rails in a fixed order. A verifier that cannot reach its store returns a 503 rather than an opinion.

The sharpest lesson came from Stripe. One Stripe account sits behind about eight of my products, and a webhook endpoint is not scoped to the app that created it: every endpoint on the account receives every event of the types it subscribes to, from every product. There is no setting to change that, which I established by checking both of Stripe’s APIs rather than by assuming. So every checkout here stamps the session and the subscription with which app it belongs to, and a guard drops anything this database does not recognise and answers 200 while it does it. A non-2xx would make Stripe retry a deliberate drop for ever.

The brand went round twice

The first brand argued with itself for a year. The Flutter prototype wore a flat cartoon chef on a loud orange tile: fine for a placeholder, wrong for a thing that wants to feel like a well-loved recipe notebook. So the rebuild went hand-painted, watercolour everything, terracotta and sage over cream. For a while the app icon was the chef, and I shipped a release that zoomed right in on her face. Then I shrank it to home-screen size and watched it turn to mush. A face dates, fights the icons either side of it, and quietly whispers “chatbot”.

What followed was a pile of dead ends: toques flat and painted, a whisk, a monogram S, sprigs, leaf after leaf. I even wrote a script to generate a parametric SVG leaf with proper depth before admitting the hand-painted one I had been sketching was better. The lesson underneath all of it: the mascot and the logo are two different jobs. So the mark became a single watercolour basil leaf, the chef stayed inside the app as the companion, and a brush-script wordmark tied the two together.

It was good work, and in August 2026 all of it went in the bin. “SousChef” was a description of a job rather than a name, and it put the second-in-command in the title of a product meant to run the kitchen. The App Store listing was the tell: it had to go up as “SousCheffe”, which is not a word and not a thing anybody would search for. The app is now The Saucery, which does three jobs in one word. A saucery is the room in an old house where the sauces were made. Sorcery is what a good kitchen looks like from outside it. And a source is where a recipe comes from, which is the feature the whole thing is built on.

Started here The original flat cartoon-chef app icon on an orange tile.
Flat chef · the placeholder
↓ the roads not taken ↓
Icon concept explored and dropped: Chef as icon.
Chef as icon
Icon concept explored and dropped: Toque.
Toque
Icon concept explored and dropped: Whisk.
Whisk
Icon concept explored and dropped: Monogram.
Monogram
Icon concept explored and dropped: Leaf, outlined.
Leaf, outlined
Icon concept explored and dropped: Leaf, painted.
Leaf, painted
↓ landed here, for ten weeks ↓
The hand-painted watercolour basil-leaf app icon that shipped as SousChef.
The basil leaf · June 2026
The retired SousChef wordmark: a watercolour basil leaf above a brush-script “SousChef”.
…and its wordmark
↓ then the name went ↓
The Saucery app icon: a swash letter S drawn in two greens on parchment.
The swash S · today
The Saucery wordmark: an engraved capital S with a long swash, set against the words “The Saucery”.
…and its wordmark

The new direction is Victorian copperplate: cross-hatched engraving, forest ink on parchment, every fixed illustration cut as line work rather than painted. Three directions were written out in prose before anybody drew anything, which is the cheapest place to find out an idea is thin. Gini drew the wordmark and the icon, and the palette came out of her files rather than out of a picker, so for the first time the brand was not mine to argue with.

Even the icon had to give something up. The full mark is an oval, a swash S and an open book, and at 1024 pixels it is a lovely thing. At the 60 pixels a phone actually draws, the book closed into a smudge and the oval into a ring. Dropping both bought the letter more than twice the height, so the app icon is the S standing on its own, and the emblem keeps the jobs it was drawn for. The S is rationed everywhere else: it is allowed in exactly three places, and a section divides with a double hairline rule rather than a flourish.

One of those three places

The home page is a saucepan, and the ribbon of steam coming off it loops once into the swash S of the wordmark. It is one engraving, cut in two at the last row above the rim so the halves recomposite exactly, and that drawing is what you get with no JavaScript. When the canvas can run, the drawn ribbon steps aside for ink that travels: a few hundred dots following the ribbon’s own centreline, traced off the plate by a script rather than redrawn, so the S the steam makes is the S in the wordmark and not a near miss. It thins as it climbs, because that is what steam does. Run a cursor through it and it parts.

An engraved saucepan, its steam curling once into the Saucery’s swash S.
The Saucery’s own home page, running here

The mascot that isn’t one

The assistant went the same way as the leaf. It used to be Sous, a chef with a backstory and a face, and a face is a promise a piece of software cannot keep: it invites people to talk to a character instead of using a tool, and it dates faster than anything else on the screen. Whisp replaced it. Whisp is a wisp of steam, which is to say it is the same S again. No face, no body, no backstory, never a mascot. It has simply been in every kitchen.

Honestly: live on one store, waiting on the other

The Saucery went on the App Store on 29 September 2026. The Android app is finished, and it is in Google Play’s closed testing, because Google will not let a new developer account publish until twelve testers have kept the app for fourteen days. If you cook on Android, leave your Google account address at thesaucery.nz and I will send you the invite.

The household is still the QA team, and the commit history shows it: fixes traced to numbered tickets filed from the apps by the people who cook with it. A report goes in from the screen it happened on, with the offending bit of the page dragged out as a screenshot, and lands in the same queue the work is scheduled from. The release on 4 October, five days after launch, carried 49 changes. The loop from “this annoyed me at dinner” to “fixed in the next build” is short enough that the family keeps filing.

Got a product that needs a backend, two native apps, and AI that pays its way? Here’s how we could work together.

Visit The Saucery