Receive report webhooks and verify the signature (API)
Register an HTTPS webhook with POST /api/v1/customers/me/webhook, read the dump_id and status it receives, verify the HMAC signature, handle retries.
Short answer: Send POST /api/v1/customers/me/webhook with a full-scope API key and a body of {"url": "https://...", "secret": "..."}. DumpTriage then sends a signed POST to that HTTPS URL each time a dump's report is delivered or its analysis fails for good. Check the X-Triage-Signature HMAC over the timestamp and the raw body, answer with a 2xx, and fetch the report when you need it. DELETE on the same path stops it.
1. Register the endpoint
curl -X POST https://api.performanceinsights.ai/api/v1/customers/me/webhook \
-H "Authorization: Bearer dts_..." \
-H "Content-Type: application/json" \
-d '{"url": "https://hooks.example.com/dumptriage", "secret": "a-long-random-string-you-keep"}'
The key must have full scope: an upload-scoped key gets 403. url must be an https address that resolves only to public IP addresses. secret is 16 to 256 characters of your choosing; keep it, because it is never returned. The response is {"registered": true}. One webhook per account: registering again replaces the URL and secret. GET /api/v1/customers/me shows the current webhook_url, never the secret.
To stop deliveries, call DELETE /api/v1/customers/me/webhook. It returns 204 and clears the URL and the secret. Email notifications are not affected.
2. What arrives
POST /dumptriage HTTP/1.1
Content-Type: application/json
X-Triage-Timestamp: 1789761600
X-Triage-Signature: sha256=<hex>
{"dump_id":"<uuid>","report_url":"<portal page for this dump>","status":"delivered"}
| Field | Meaning |
|---|---|
dump_id | The id that POST /api/v1/dumps returned. Fetch the report with GET /api/v1/dumps/{id}/report. |
status | delivered: the report is ready. failed: the analysis failed for good. |
report_url | The portal page for this dump, the same link the upload response returns. It opens for anyone signed in to the account. |
connector_group_url | Only on a dump that came in through a crash-platform connector: that platform's own link back to the crash group. |
3. Verify the signature
The signature is the HMAC-SHA256 of the timestamp, a literal . and the raw body bytes, keyed with your secret. Use the bytes exactly as received: do not parse and re-serialize the JSON. Compare in constant time and reject a timestamp more than five minutes old, so a captured delivery cannot be replayed later.
import hashlib, hmac, time
def verify(secret: str, headers, raw_body: bytes) -> bool:
ts = headers["X-Triage-Timestamp"]
if abs(time.time() - int(ts)) > 300:
return False
expected = "sha256=" + hmac.new(secret.encode(), f"{ts}.".encode() + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, headers["X-Triage-Signature"])
4. Answers, retries and limits
- Answer with a
2xxwithin 20 seconds. That budget covers connecting and your response together. - Any other answer, a timeout or a failed connection counts as a failed delivery. Redirects are not followed.
- A failed delivery is retried with exponential backoff: by default 30 seconds after the first failure, doubling up to a one-hour wait, for up to 12 attempts. After the last one the delivery is dropped; the report stays available through the API and the portal.
- The address is checked again at every delivery, and the request goes to the addresses that check approved. A host whose DNS now answers a private or internal address is not called.
- Retries mean you can see the same
dump_idandstatustwice: treat the handler as idempotent.
Errors on these calls
| Status | When |
|---|---|
400 | The url is not accepted: not https, no host, or it resolves to a non-public address (on register). |
401 | Missing or invalid API key. |
403 | The key's scope or the account does not allow it, for example an upload-scoped key. |
422 | Missing url or secret, or a secret outside 16 to 256 characters. |
Every error body is {"detail": "..."}.
Common questions
- Which URLs can I register as a webhook?
- An https URL that resolves only to public addresses. http URLs, hosts that resolve to private or internal addresses, and redirects are not accepted; the address is checked again at every delivery.
- What does DumpTriage send to my webhook?
- A POST with a JSON body of dump_id, status (delivered or failed) and report_url, signed with your secret in the X-Triage-Timestamp and X-Triage-Signature headers. A dump that came in through a crash-platform connector also carries connector_group_url.
- What happens if my endpoint is down?
- A delivery that does not get a 2xx answer within 20 seconds is retried with exponential backoff: 30 seconds after the first failure, doubling up to one hour, for up to 12 attempts by default. After that the delivery is dropped; the report is still available through the API and the portal.
Ready to send a dump?
Upload a minidump to DumpTriage. It runs the debugger, reads the output and sends back the faulting module, the stack and a plain-language root cause (how the analysis works). Except on the Solo plan, cases the automated passes can't settle go to an engineer. Free during the pilot.
Create an account