cd /news/developer-tools/my-ai-agent-ran-a-full-sdlc-on-one-s… · home topics developer-tools article
[ARTICLE · art-94084] src=blog.spill.coffee ↗ pub= topic=developer-tools verified=true sentiment=· neutral

My AI agent ran a full SDLC on one Shopify webhook capture

A developer used FlurryPORT's webhook capture and replay service to fix a Shopify integration that failed to process cancellations and repeatedly emailed humans about unresolved fulfillment errors, completing the work without placing a second test order. The fix involved adding a watch for cancellation topics and clearing error rows after a single notification, addressing a two-year-old gap in the client's dropship order flow.

read8 min views1 publishedAug 12, 2026
My AI agent ran a full SDLC on one Shopify webhook capture
Image: Blog (auto-discovered)

I never placed a second test order. The first one is still doing the work.

A client’s Shopify storefront sends orders to a dropship manufacturer. Two things were wrong with that integration, and a request to fix one provided me the bandwidth to fix both.

The ask was to fix cancellations. The cancellation integration had been part of the design and was dropped to make a deadline, which is a decision every developer reading this has made at least once. Cancellations came in by phone, they were rare, and the manufacturer preferred it that way, so the gap held for two years. Nobody told me what changed, and I did not ask. Two quiet years and then a request is its own kind of answer.

There was a standing delay built into the system, thirty minutes, before an order shipped to the manufacturer. It was in place for this very thing: to let an order be cancelled before the manufacturer was notified. The window was there. The application was not listening for cancels.

A customer cancels, Shopify records it, the integration goes on believing the order is live, and somewhere down the line the thing gets built and boxed. Nobody finds out until the customer asks where their refund went, or why the thing turned up anyway.

The second problem was louder, at least to me. When Shopify rejected a fulfillment, the system emailed a human for help. That part was deliberate: scope and timing again, and handing off to a person is a reasonable place to stop when you have to stop somewhere.

The oversight was narrower. The error rows never cleared, so the next run found the same unresolved row and sent the same email, and it kept doing that until somebody applied a database update by hand.

This job is what bought the time to close it properly: notify once, then heal itself.

Brief inspection of the plumbing

Before the day itself, an explanation of how it is plumbed. The rest of this post leans on a handful of words, and it goes faster if they mean the same thing to both of us. FlurryPORT has adopted the following terms to describe its product:

Capture. One recorded delivery: everything that arrived, stored byte for byte.Endpoint. Your capture URL, the stable address you point a sender at.Target. Where a capture goes next: any HTTP address outside FlurryPORT.Replay. A capture delivered to a target.Auto-forward. A replay nobody triggered, because the endpoint is configured to deliver the moment a capture lands.Watch. A standing filter on an endpoint. Every arriving capture gets checked, and the ones that match carry its label.

A full list of FlurryPORT’s nomenclature can be found here.

The harness

Testing a webhook requires setting up a pipe. I pointed the dev Shopify app at a FlurryPORT endpoint built to capture the topics I cared about. Topics defined in a Shopify app’s TOML require an absolute URL to talk to, and that is exactly what an endpoint provides. No surgery on the app URL, and no tunnel to keep alive between sessions.

I set up three watches on the pipe, each tailored to a topic the client’s app cared about. To define a watch filter, a JSONata predicate is used to inspect incoming requests. In this case the x-shopify-topic header is enough to route pipe traffic. Cancellations arrived labelled and ready to be replayed to localhost.

That is the whole harness: one endpoint and three watches. On the current limits it fits the free plan.

Byte for byte

FlurryPORT can validate HMAC signatures at the endpoint. I did not switch it on, because there was no reason to. Shopify puts its signature in a request header, that header travels with the capture, and the payload underneath it is stored, and transferred, byte for byte. So validation happened where it always could have: on the client’s backend on localhost, against the copy of the secret already sitting there.

The webhook secret never left localhost. Had I wanted the endpoint to turn away anything not signed by Shopify, FlurryPORT would have needed a copy. It was not necessary. The application was already configured to validate the byte-exact Shopify requests. My infrastructure delivered them unaltered: the first time I have used it for client work instead of a controlled demo.

Which is also why the next part is embarrassing.

The bug I came to debug

The first replays failed.

I braced for an afternoon of debugging and was delighted when my AI agent read the receipt and answered immediately.

The FlurryPORT CLI authenticates through a PAT that you issue. flurryport login fp_...

saves the token used by an AI agent to make requests via the CLI’s MCP implementation. As a precaution, the standard settings on a token redact PII, a privacy scope. The scope makes a best-effort attempt to mask personal data. Order payloads are full of things that look like personal data, on account of being full of personal data.

Which means the bytes arriving at localhost were not the bytes Shopify had signed, so verification failed exactly as it should have, and the client’s backend returned a 401.

I was there to debug a webhook integration. The symptom was a 401 from the webhook consumer. That is not a FlurryPORT-shaped error at all, it is precisely the error I would have gone hunting for in the client’s code, and I would have found nothing wrong with the client’s code, because there was nothing wrong with the client’s code. My tool’s fault, wearing my client’s bug’s face.

The receipt knew the whole time. Every replay comes back with one, and it carries a flag saying whether the payload was redacted on the way out. It was sitting there, set, on every failing attempt. Nothing about that needed reasoning. It needed reading, and the AI agent did not skip a beat and asked for an upgraded token.

I re-issued the PAT without the masking scope, and every replay after that verified.

Order #1090

I placed a real order, cancelled it, and both captures landed in my FlurryPORT pipe.

A quick description to my AI agent about the problem we were here to solve, and off to the races. My agent went to work sending, and re-sending, the captures through the application, reproducing the problem and verifying its fix. A request for unit tests and it snagged the captures once again to build byte-for-byte stubs to run through the test suite.

The whole path needed re-verifying, and the instinct after a lifetime of this work is immediate and almost physical: go click through the store, place another order, cancel it.

I did not do that. I did not need to: FlurryPORT still had the captures, waiting to finish the job.

The cancel capture for order #1090 got recycled to do integration testing. Local testing is fine, but I still stand up a minimum of one extra environment to catch things that are not reproducible on my development machine. I did not have to reconfigure the development Shopify store to point at the hosted environment. I changed the target and completed integration testing with the same #1090 captures.

To summarize the day, those captures drove three separate scenarios. A happy path on intake. Pre-30-minute, the cancellation window. Post-30-minute, order already sent to the manufacturer, the escalation email path. Then a suite of unit tests with fixtures matching the Shopify payloads.

No second order was placed. The optimization of webhook captures was proven.

Some quick bookkeeping on tests

The repo had zero tests on Sunday morning. It had thirty by Sunday night, green on the first run, and seventy-three by the time the weekend was over.

Real payloads teach things hand-written mocks never will. The update that trails a cancellation arrives carrying financial_status: "refunded"

, which I would not have thought to mock and would have got wrong if I had. The thin cancel payload provably cannot deserialize as a create or an update, which sounds like trivia until you realise it is a free type check the provider is handing you for nothing. I spent a while reading those payloads for pleasure. There is a lot of accumulated decision-making in a webhook body if you slow down enough to look at it.

What the job made me build

None of this was product planning. I did not sit down to design anything. The development session handed me the templates. The configuration I stood up to debug the integration turned out to be three recipes in rough form, and I wrote them up properly the next day.

Three of them, and all three are live. ** capture-fixture-suite** turns captured provider traffic into a test suite whose fixtures can prove where their bytes came from.

diverts your app’s outbound call to a capture URL, so you can read exactly what you send a vendor without sending it. And

vendor-submission-tap vendor-callback-simulatorruns the pipe backwards, reshaping a captured submission into the vendor’s own callback shape and delivering it to your app, so the return leg is testable with nobody on the other end.

All three of them fit on the free plan together, with nothing to spare.

I built FlurryPORT on the belief that a captured request is worth keeping. It took a real job, with a real deadline and somebody else’s customers on the other end of it, to turn that belief into three specific things you can install this afternoon.

If any of that sounds like a weekend you have had, the walkthroughs are at flurryport.io/recipes, flurryport.dev/try takes about two minutes, and the honest numbers are at flurryport.io/pricing.

── more in #developer-tools 4 stories · sorted by recency
── more on @flurryport 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/my-ai-agent-ran-a-fu…] indexed:0 read:8min 2026-08-12 ·