Replay with Edit — Fix the Payload, Transform, or Destination, Then Retry
Plain replay re-runs the same broken inputs against the same broken destination — guaranteed to fail the same way unless the failure was transient. Today every failed delivery gets an Edit & Replay modal: change the payload, the transform code, the destination, or the outbound headers for this one retry, all optional.
What Was Wrong With Plain Replay
The "Replay" button on a failed delivery used to do exactly one thing: re-queue the original event for the original destination using the route's current transform. If the cause of the failure was anything other than a transient destination blip, you were guaranteed to hit the same failure again. Replaying a delivery that failed because your transform broke just means you watch it fail twice.
So when we talked to teams using Hookbase to deliver real production traffic, the same workflow kept coming up:
- Find the failed delivery.
- Copy the payload.
- Edit it locally or fix the transform code in the route settings.
- Wait for the cache to expire.
- Click replay.
- Hope.
That's not recovery — that's reconstruction. Today we're shipping the primitive that fixes it.
Replay With Edit
Every failed delivery now has an Edit & Replay modal. You can change four things, all optional, all applied to this one replay only:
- Payload — paste in an edited version of the source payload. The original event stays untouched.
- Destination — send to a different destination than the route's normal one. Common case: replay production failures to a staging URL while you investigate.
- Transform — paste new JSONata or JavaScript code. Runs only against this replay, no route changes.
- Outbound headers — override or add headers (with the usual
Host/Content-Lengthreservations).
Submit, and a fresh delivery hits the queue with your overrides applied. The original delivery row stays in place; nothing about the route's persistent configuration changes.
"Actually, Save That Transform"
The most common follow-up after a successful replay is "great, now save that transform code on the route so it stops failing." That used to require flipping back to the route settings, pasting the same code, saving, then crossing your fingers about cache TTL.
The modal has a checkbox: "Save this code to the route's transform." Tick it, and after the replay succeeds we update the route's attached transform with your new code and invalidate the worker's cached route immediately. The replay you just submitted still uses the override code directly — so you get to verify before the change takes effect for everyone else.
(If the route doesn't have a transform attached yet, the checkbox is disabled and we explain why. Create a transform via the normal flow first, then this option becomes available.)
Validation We Do For You
Replays with overrides are a footgun if we don't have guardrails:
- Transform code is capped at 64KB so a runaway paste can't blow up the queue message.
- Header overrides cap at 50 entries and reject hop-by-hop headers and ones the worker controls (
Host,Content-Length,Transfer-Encoding,Connection). - Destination overrides must belong to the same org — you can't accidentally route to someone else's destination.
persistTransformis gated behind also settingtransformOverrideand the route already having a transform attached, so we never quietly create or attach a new transform from a replay.
Every override is logged in the audit log with the source delivery id, the route, and the actor. Persisting a transform creates a separate audit entry distinct from the replay itself.
API Shape
For automation, the same primitive is available at POST /api/organizations/:orgId/deliveries/:deliveryId/replay:
{
"modifiedPayload": { "...": "..." },
"destinationOverride": "staging-webhook",
"transformOverride": { "code": "$ ~> | $ | { } |", "type": "jsonata" },
"headersOverride": { "X-Replay-Reason": "post-incident" },
"persistTransform": false
}
All fields are optional. The response includes the new delivery id so you can poll it until it finishes.
Why This Matters
The whole point of a webhook relay is that integration debugging is hard, and you want a single seam where everything observable happens. Replay-with-edit turns that seam into a workbench: see what broke, edit the inputs, retry, confirm green, optionally persist. The full "3am pager" loop in under a minute.
This is the primitive we'll build the rest of the inbound debugging UX on top of. Cluster-wide edit-and-replay, transform-diff against recent failures, alert-on-this-pattern — they're all the same primitive applied at different scopes.