Custom Webhook: deliver to any endpoint
The Custom Webhook integration delivers every share you create as a structured, HMAC-signed JSON payload to any public https URL you own — automation platforms (n8n, Zapier, Make, IFTTT), your own backend, ticketing systems, or a bot relay.
One URL is all it takes: no app registration, no credentials to obtain. A server-generated HMAC secret lets your consumer verify that requests really come from BugCapturer.
Step 1: Get a Webhook URL from your target
Section titled “Step 1: Get a Webhook URL from your target”Create a Webhook trigger/receiving node in your target system and copy its https URL:
- n8n: add a Webhook node → use the Production URL (method: POST)
- Zapier: build a Zap with the Catch Hook trigger and copy the hook URL
- Make: add a Custom webhook module and copy its URL
- Your own system: expose any public https endpoint that accepts POST with JSON
Step 2: Add the integration in BugCapturer
Section titled “Step 2: Add the integration in BugCapturer”- Sign in at app.bugcapturer.com and open the “Integrations” page
- Select “Custom Webhook” in the left platform list → click “Add integration”
- Fill in a name and the Webhook URL (https, public)
- Click “Verify & save” — the backend validates the URL (public https only) and sends a signed test event (
event=test) to your endpoint - When the test succeeds, the HMAC secret is shown once — copy it into your consumer’s configuration immediately. Only the last 4 characters are shown afterwards; you can rotate it anytime via the card menu → “Rotate secret”
What BugCapturer sends
Section titled “What BugCapturer sends”Every share triggers a POST request with Content-Type: application/json (UTF-8), payload schema v1:
{ "event": "share.created", "source": "BugCapturer", "schema_version": 1, "sent_at": "2026-09-14T21:30:00+08:00", "share": { "token": "a1b2c3d4", "report_url": "https://app.bugcapturer.com/share/a1b2c3d4", "screenshot_url": "https://api.bugcapturer.com/api/shares/a1b2c3d4/file", "submitted_at": "2026-09-14T21:29:58+08:00", "expires_at": "2026-12-13T21:29:58+08:00" }, "report": { "description": "Submit-order button does not respond", "feedback_type": "", "page_url": "https://example.com/checkout", "page_title": "Checkout - Example Shop", "browser": "Chrome 128", "os": "Windows 11", "screen": "1920x1080", "console_errors_count": 2, "network_errors_count": 0 }}| Field | Meaning |
|---|---|
event | share.created (a share was created) or test (save check / “Test” button) |
schema_version | Always 1; bumped only on breaking changes, so consumers can stay compatible |
share.token | Unique share token |
share.report_url | Full report page (opens in browser, link does not expire) |
share.screenshot_url | Screenshot proxy URL (302 to a signed storage URL) |
share.expires_at | When the share expires and its files are deleted |
report.description | Problem description, truncated at 2000 chars |
report.page_url | Sanitized URL of the page where the report was captured |
report.feedback_type | Reserved — always empty in schema v1 (the extension form no longer collects a report type) |
report.*_errors_count | Counts only — for privacy, console/network error contents are never included; open report_url for the full details |
| all timestamps | ISO 8601 with +08:00 offset |
Verifying the signature
Section titled “Verifying the signature”Every request carries the header:
X-BugCapturer-Signature: t=<unix timestamp>,v1=<hex digest>where v1 = HMAC-SHA256(secret, "{t}.{raw_request_body}"). To verify, recompute the HMAC over "{t}." + raw body and compare with a constant-time equality check. Also reject timestamps deviating more than 5 minutes from your clock (replay protection).
Python:
import hmac, hashlib, time
def verify(secret: str, header: str, raw_body: bytes, tolerance: int = 300) -> bool: parts = dict(p.split("=", 1) for p in header.split(",")) if abs(time.time() - int(parts["t"])) > tolerance: return False # outside clock tolerance — possible replay expected = hmac.new(secret.encode(), f'{parts["t"]}.'.encode() + raw_body, hashlib.sha256).hexdigest() return hmac.compare_digest(expected, parts["v1"])
# Flask example@app.route("/webhooks/bugcapturer", methods=["POST"])def hook(): if not verify(SECRET, request.headers["X-BugCapturer-Signature"], request.get_data()): return "bad signature", 401 payload = request.get_json() ... # route into your workflow return "", 204Node.js:
const crypto = require("crypto");
function verify(secret, header, rawBody, tolerance = 300_000) { const parts = Object.fromEntries(header.split(",").map((p) => p.split("="))); if (Math.abs(Date.now() - Number(parts.t) * 1000) > tolerance) return false; const expected = crypto .createHmac("sha256", secret) .update(`${parts.t}.${rawBody}`, "utf8") .digest("hex"); return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));}
// Express example (needs the raw body)app.post("/webhooks/bugcapturer", express.raw({ type: "application/json" }), (req, res) => { if (!verify(SECRET, req.get("X-BugCapturer-Signature"), req.body)) { return res.status(401).send("bad signature"); } const payload = JSON.parse(req.body); ... // route into your workflow res.status(204).end(); });Managing the secret
Section titled “Managing the secret”- Shown once after creation (and after rotation); the card afterwards shows only
······<last 4> - Rotate secret (card menu) revokes the old secret immediately — update your consumer, then click “Test” to confirm
- The secret never appears in delivery logs, error messages, or emails
“URL not valid” on save?
Only public https URLs are accepted (no http://, no localhost/private IPs, no userinfo in the URL). This protects our network from SSRF.
Test delivery fails with 401/403?
Your endpoint requires its own authentication. Either allow requests carrying the X-BugCapturer-Signature header, or put an auth token in the URL path accepted by your system (n8n / Zapier hook URLs already embed one).
Can one report go to several integrations? Yes — configure multiple integrations, then pick the target with the “Sync target” selector in the extension before sharing. Only the selected integration receives that report.