I tried to sell coffee through my own product
Where transformations stop, why nobody learns your UI, and the catalog that came out of it.
I had just added transformations to webhook captures, and I wanted to see how far that went on its own. A webhook lands in FlurryPORT, you reshape it on the way through, and it leaves looking like whatever the next system expects. That is a lot of plumbing to hand somebody, and I didn’t know yet where it stopped being enough, so I went looking for something real to break it against.
My search turned up Roastify, a company that will roast coffee, put your label on the bag, and ship it to whoever you tell them to. My blog is called spill.coffee, a fairly easy test case. I made an account with them and set up a Square store.
The shape was simple. Somebody buys a bag through Square, Square fires a webhook, FlurryPORT catches it, the transformation reshapes it into whatever Roastify’s side expects, and a bag of coffee goes out with my name on it. No server of mine anywhere in that, which was the entire thing I was trying to prove.
It didn’t work. The honest version is that I never fully diagnosed the ‘why’. I’m fairly sure the gap opened up somewhere on the way from Square to Roastify, but I stopped short of proving it, because by then the shape of the problem was clear enough that its exact location had stopped mattering.
A purchase webhook doesn’t really contain a purchase. It carries identifiers. Which order, which customer, which line items, every one of them pointing at a record living somewhere else. To tell a roaster what to grind and where to ship it, I’d have to take those IDs, go back and ask for the rest, assemble an answer out of the replies, and only then deliver anything. A lookup in the middle. That’s an application, and I didn’t want to write one, and I couldn’t picture anyone else wanting to write one either.
Roastify publishes a Shopify app. I knew that going in and had gone around it on purpose, because the whole question was whether a pipe and a transformation could stand in for one. What I came away with was a fairly clear answer about why that app exists.
That left a question I liked considerably less than the technical one. Who is going to learn my interface? A developer with a webhook that won’t fire will learn anybody’s interface at two in the morning, and that was the original idea. I could envision more.
What I actually needed was a way to teach people to use the thing for their own purposes rather than mine. I had documentation already, and documentation wasn’t the problem. Knowing what every control does is a long way from knowing what to do with them, and I was the only person who had ever walked that distance.
I could half-see an answer in MCP, though I hadn’t gone anywhere near the whole of it yet. The first pass was small and unambitious. Get an agent to stand up a simple pass-through pipe, nothing clever, just the boring version done in conversation instead of in my interface.
What I couldn’t settle was whether it would generalize. I could think of plenty of things I’d want to do with a pipe, and I could probably build every one of them, but I had no way of knowing whether my imagination would line up with a stranger’s curiosity. The uses I could picture were the uses a person who built the product could picture, which is a narrow and badly calibrated sample.
I wrote all of them down
So I stopped trying to pick the right one. I wrote down every use I could think of, one page per job, and published the lot of them on the theory that whatever people reached for would tell me more than anything I could reason out. I started calling them recipes.
A recipe is a page that explains one job you can do through a secured pipe, written so an AI agent can carry it out: post to Slack without the bot token ever reaching the model, push a notification to your phone, file a GitHub issue with a token the agent never touches.
The catalog says it more plainly than I just did. Your agent finds a recipe, you paste one credential on a secret page, and every delivery comes back with a receipt. That’s the whole interaction. Nobody opens my interface, which was the point.
It’s a different product than the one I started with, though nothing underneath it changed. The pipes are the same ones I built for debugging. All that’s new is that the instructions have a reader who isn’t me.
The key never moves
The security in a recipe is designed to remove the temptation to hand an agent your credentials.
Think about how you’d do it otherwise. You want your assistant to file an issue in GitHub, so you paste a personal access token into the chat. It works. It also means that token is now sitting in the transcript, in whatever logs live behind that transcript, in your scrollback where you’ll forget about it, and in the model’s context for the rest of the session. You solved a five-minute problem by handing out a key with no expiry and no way to watch what it opens.
A recipe puts the key somewhere else. Your agent generates a secured page, hands you the link, and you paste the credential there once. From then on it refers to that secret by name and never sees the value. It asks the pipe to do the thing, and the pipe is what holds the credential. If the far end echoes the secret back in its own response, the value gets scrubbed before that response is stored, so it doesn’t surface later in something you kept for debugging.
And you get to see what it did. The capture machinery I built for debugging is the same machinery that records every delivery, so each time the agent uses a credential it has no access to, a receipt lands with the payload that went out and what came back. Whatever the agent tells you it did, the traffic is sitting right there.
A recipe buys you something besides safety. An agent working without one explores. It reads, guesses, makes a call, reads the error, adjusts, and tries again, and all of that thrash costs you turns and context. A recipe is the walkthrough it would otherwise have had to derive, so it stops deriving.
It doesn’t make the agent trustworthy. Something that can post to Slack can still post something stupid to Slack, and no amount of credential hygiene fixes that. What it can’t do is walk off with the key and use it somewhere I never agreed to.
One of them, start to finish
It’s easier to show one than to describe the category, so here’s the smallest useful recipe I have.
Say you want your phone to buzz when something finishes. A long build, a nightly job, a deploy you’d rather not sit and watch.
You point your agent at FlurryPORT once with npx flurryport mcp, give it a read/write agent token, and ask it for flurryport:ntfy-push. It reads the recipe. From your side, that’s the whole setup conversation.
ntfy works on topics. You pick a long random one, subscribe to it in the ntfy app on your phone, and after that anything sent to that topic turns up as a notification. The topic name is the only thing standing between your pocket and the rest of the internet, so you treat it as a password rather than a label. If you’d rather lock it down properly, you generate an access token at ntfy.sh, and when the agent asks for it you paste it on a FlurryPORT secret page where it stays encrypted server-side.
From then on the agent has one new tool, flry_ntfy_push. It takes a message, and optionally a title and a priority level. When it fires, FlurryPORT fills in the topic you chose and resolves the credential at delivery, posts to ntfy, and hands back a receipt the agent can point at later.
Then your phone buzzes.
Nothing in there is clever, which is what I want from it. It does what you could have done yourself with an afternoon and the ntfy documentation, minus the afternoon. Somebody wrote it down in a form your agent can execute, and the secret stayed out of the conversation the whole way through.
Three things I didn’t plan on
The catalog has taught me more than I put into it.
A recipe is a page, and pages get indexed. Nobody had heard of flurryport.io. Search engines have no reason to trust a domain that turned up this year, and the standard remedy is to go be a person on the internet: post, comment, build an audience, accumulate some karma, get linked to by people who already have both. I’m a developer who doesn’t post, which describes most of us, and that advice assumes a personality I don’t have and wasn’t going to develop on a deadline. What I had instead was a stack of specific pages describing specific problems, written in the words somebody would actually type when they had one. Product and search surface in a single artifact, which is the only kind of marketing I’ve found I can stand to do.
The second thing I noticed reading my own catalog end to end. The same promise turns up in almost every entry, worded a little differently each time. The token stays server-side. The model never sees it. A fine-grained PAT it never touches. Never enters the conversation. I didn’t set out to write that sentence eighteen ways, and I only saw that I had once they were all sitting on one page together.
The third one I’m still chewing on. There are eighteen recipes. Fifteen are about doing something to a service. Three are about agents coordinating with each other. None of them are the thing I built the product to do.
FlurryPORT started as a webhook catcher, and it started there because I had a client with a Shopify store feeding orders to a manufacturer and no good way to see what was happening in between. That’s still what the pipes are for. I spent this past weekend on that same integration.
Which is the part that got me. Eighteen recipes, and not one of them describes what I spent my weekend doing.
The same product, the other way round
The coffee store failed because a webhook payload doesn’t contain what you need to act on it. The weekend worked because when you’re testing, the payload is all there is.
One real order, cancelled once. That single captured cancellation drove three separate checks: the path that was supposed to work, an authorization check I forced by making the store look uninstalled, and an escalation path that only fires when the database already looks like the order went out. Three scenarios, one order, and I never went back to the store to ask for another.
The first replays failed, and that was my own doing. The token that authenticates my agent to FlurryPORT, the one the CLI hands you when you set up MCP, defaults to masking personal data on its way through. That is a sensible posture when you’re getting started, and it is also precisely the thing a signature exists to catch. It surfaced as a 401 that looked like it came from the consumer, and the forward receipt is what told me the altered bytes were mine. I switched to a token without the masking scope, and from there the signatures survived every replay. The client’s webhook secret is a different thing entirely, and it never left their environment at any point.
Then those same captures became the first tests that repository has ever had. It started the weekend with none and finished with seventy-three, several built from byte-exact provider payloads with a guard that fails the build if anybody reformats them.
That’s the same product that couldn’t sell me a bag of coffee, and nothing about it had changed. Testing never asks you to invent what isn’t in front of you. The capture is the whole of the thing, which is what makes it useless for running a store and ideal for proving one works.
If any of that sounds like a week you’ve had, the catalog is at flurryport.io/recipes. ntfy-push makes your phone buzz when a build finishes. slack-post lets your agent talk to a channel without ever seeing the bot token. azure-devops-manage creates and updates work items in one batched pipe, priority and state and sprint included. flurryport.dev/try takes about two minutes, and the honest numbers are at flurryport.io/pricing.




