Integrations
Webhooks you can verify.
Register an endpoint and Disputek delivers every event to it as an HMAC-signed POST. No polling, no SDK, no inbound access to your data - your server receives a request and decides what to do with it.
What you get
Three events cover the whole chargeback lifecycle. Subscribe to the ones you need; each endpoint keeps its own subscription list, so a test endpoint can listen for one event without inheriting the rest.
- analysis.completedA document bundle finished analysis. The payload carries the run summary: exposure, disputable versus valid, and the recovered amount.
- chargeback.updatedA chargeback changed state or classification - reclassified, disputed, filed, or won.
- test.sentA delivery you triggered from the dashboard. The payload carries only a confirmation message; use it to prove the path end to end.
Register an endpoint
- 1Open Integrations in the Disputek dashboard and go to the Webhooks tab. This is a paid-plan feature, alongside Zapier, n8n, and the MCP server.
- 2Add an endpoint: give it a name your team will recognise later, paste an https:// URL you control, and tick the events it should receive.
- 3Save it. Disputek generates a signing secret for the endpoint - a whsec_ value shown once, at creation. Store it the way you store any credential; if you lose it, delete the endpoint and create a new one.
- 4Press Test. Disputek delivers a test.sent event immediately and shows the delivery result, so you can confirm the signature check before any real event fires.
Only public https destinations are accepted. Before every attempt - not just at creation - Disputek re-checks that the hostname still resolves to a public address, and it never follows redirects. An endpoint that resolves privately is rejected rather than called.
Verify the signature
Every delivery is a JSON POST with a small set of headers. The signature is an HMAC-SHA256 of the raw request body, keyed with your endpoint secret, hex-encoded and prefixed with sha256=.
content-type: application/json x-disputek-event: analysis.completed x-disputek-delivery: <delivery id> x-disputek-timestamp: 2026-01-14T09:12:33.481Z x-disputek-signature: sha256=<hex digest>
The body is a three-key envelope, so a consumer can branch on the event without inspecting headers:
{
"event": "analysis.completed",
"occurredAt": "2026-01-14T09:12:33.481Z",
"data": { }
}Verify before parsing, and compare in constant time. This is the part that matters: a comparison with === leaks timing information, and parsing first means you act on a body you have not authenticated.
import { createHmac, timingSafeEqual } from "node:crypto";
// Read the body as bytes - the signature covers the exact payload,
// not a re-serialized copy of it.
const raw = Buffer.from(req.rawBody);
const provided = Buffer.from(req.headers["x-disputek-signature"] ?? "");
const expected = Buffer.from(
"sha256=" + createHmac("sha256", process.env.DISPUTEK_WEBHOOK_SECRET)
.update(raw)
.digest("hex"),
);
// timingSafeEqual throws on a length mismatch, so guard first.
const ok =
provided.length === expected.length && timingSafeEqual(provided, expected);
if (!ok) return res.status(401).send("bad signature");
const { event, occurredAt, data } = JSON.parse(raw.toString("utf8"));Delivery semantics
- Answer 2xx and Disputek stops. Any other status is recorded as a failed delivery.
- 5xx and network errors are retried up to four attempts, backing off 1s, 10s, then 60s. A 4xx is not retried - your endpoint understood the request and refused it, so repeating it would not change the answer.
- Each attempt times out after 10 seconds.
- A dead endpoint never blocks analysis.Delivery is fire-and-forget: a failing webhook is recorded on the delivery log and swallowed, so your uploads and dispute runs finish on time regardless.
- Every attempt is inspectable. The delivery log keeps the payload, attempt count, status, and last error per delivery, so you can debug from the dashboard instead of guessing.
Prefer to pull than to receive?
Webhooks are the right default when something should happen without you asking. If you would rather poll on your own schedule, the same API key backs a read endpoint - this is what the Zapier trigger and the n8n template call:
curl -X POST https://api.disputek.com/api/integrations/chargebacks \
-H "Authorization: Bearer dk_..." \
-H "Content-Type: application/json" \
-d '{"limit": 25}'It accepts since (an ISO timestamp floor), search (chargeback id, PO number, or retailer), and limit (capped at 100). Create a key under Settings - API Keys; it is shown once and can be revoked at any time.