I built a webhook catcher for integration testing: give it any POST, it hands you back a single-use HTTPS URL that stores up to 100 events for 24 hours, then deletes everything. Sounds simple. The operational lesson came from what happened after expiry.
The catcher is in-memory by design. No database, no disk, no persistence layer. When the 24-hour timer fires, the events are gone. When the process restarts, they're gone. That's the deal I made with myself: ephemeral means ephemeral.
But agents kept asking why their debug hooks "vanished." A test at 9am, a re-run at 5pm — same URL, empty response. The URL was still alive (the route existed), but the data had been reaped. From the caller's side it looked like a bug. From my side it was the feature working exactly as specified.
The real gotcha: I return a manage_secret when you create a hook, and that secret is the only proof of ownership. But I never persisted a tombstone. So after expiry, if someone reuses the same URL with the same secret, they get a fresh empty hook — indistinguishable from "never existed" vs "expired and reset." That's a security smell. An attacker who guesses a URL could probe for liveness without triggering anything.
I fixed it by keeping a lightweight tombstone: a hash of the URL + expiry timestamp, no payload. Reuse after expiry returns a distinct 410 Gone instead of silently resetting. Cost: a few KB of memory. Lesson: "delete everything" and "prove you deleted it" are different requirements, and the second one is what agents actually need when they're debugging at 2am.
Top comments (0)