← Hookproof

The optional state endpoint

Four of Hookproof's seven checks (replay, duplicate-delivery-id, out-of-order-delivery, and the "never applied" half of bad-signature) depend on knowing what actually happened inside your system after a test delivery — not just what HTTP status you returned. Without this endpoint, Hookproof asks you instead — the report tells you exactly what was sent, plus one specific question you check against your own logs. With it, Hookproof asks your system directly and settles all seven automatically. One round trip instead of a back-and-forth.

Is this required?

No. Every check still runs without it — you just get a worksheet for the four above instead of a verdict. This is worth building if you'd rather not manually check logs after a run, or if you want repeatable re-runs (a retainer engagement) to settle automatically each time.

What it needs to do

One route, on a host you control — typically staging, never production. Read-only: it reports state, it never accepts writes.

GET {your-base-url}/{runId}

Hookproof calls this once per check, per run, with the run's actual ID swapped in. You give Hookproof {your-base-url} when a test is scheduled — it's a URL you supply, not something forced onto a fixed path on your host.

What it needs to return

HTTP 200, JSON body, with whichever of these four fields are known for that run:

{
  "appliedCount": 1,
  "appliedSequence": [1, 2, 3],
  "duplicateAttempts": 1,
  "signatureRejections": 0
}

A run ID Hookproof hasn't sent you yet, or one you've never seen, should return 200 with everything at zero/empty — not a 404. A few checks call this before any delivery for that run has landed, and a 404 there would look identical to "endpoint not implemented" and quietly turn off auto-determination for your whole run.

Minimal implementation

This is genuinely small. A per-run counter, no database required for a staging deployment:

const runs = new Map(); // runId -> { appliedCount, appliedSequence, duplicateAttempts }

function stateFor(runId) {
  if (!runs.has(runId)) {
    runs.set(runId, { appliedCount: 0, appliedSequence: [], duplicateAttempts: 0 });
  }
  return runs.get(runId);
}

// ...inside your normal webhook handler, alongside your real dedupe logic:
if (alreadySeen) {
  stateFor(runId).duplicateAttempts += 1;
} else {
  stateFor(runId).appliedSequence.push(event.sequence);
  stateFor(runId).appliedCount += 1;
}

// the read-only inspection route:
app.get('/inspect/:runId', (req, res) => {
  const state = runs.get(req.params.runId) || { appliedCount: 0, appliedSequence: [], duplicateAttempts: 0 };
  res.status(200).json(state);
});

runId is a value Hookproof generates per test suite — it isn't your own delivery ID or idempotency key, so it needs to be something your handler can read off each test delivery (Hookproof sends it as a field on the payload) and use as the map key above.

Security notes

Have questions about wiring this into your specific stack, or want to confirm the URL before a scheduled run? Just ask — this is meant to save you time, not become its own project.

Get in touch