Webhooks
Ship report payloads to any HTTPS endpoint. Useful for archiving to S3, piping into your own workflow, or triggering downstream automations.
Webhook delivery is a Pro feature.
Set up a webhook destination
- Go to your report → Delivery → Add destination.
- Pick Webhook and paste your HTTPS URL.
- Copy the generated signing secret and store it in your app's environment (we only show it once).
- Send a test payload from the dashboard to verify.
Payload
We POST a JSON body with Content-Type: application/json.
{
"id": "run_7f9a...",
"report_id": "rep_abcd1234",
"title": "Weekly engineering digest",
"generated_at": "2026-04-25T16:00:00Z",
"schedule_cron": "0 16 * * 5",
"sources": ["github", "linear"],
"destinations": ["webhook"],
"summary": "Team shipped 12 PRs this week...",
"content": {
"markdown": "...",
"html": "..."
},
"metrics": {
"prs_merged": 12,
"issues_closed": 7,
"incidents": 2
}
}Headers
| X-AutoReport-Event | Always report.delivered. |
| X-AutoReport-Signature | HMAC-SHA256 of {timestamp}.{body}. |
| X-AutoReport-Timestamp | Unix timestamp (seconds) when the request was signed. |
Verify signatures
Always verify the signature before trusting the payload. Example in Node.js:
import crypto from "crypto";
const signature = req.headers["x-autoreport-signature"];
const timestamp = req.headers["x-autoreport-timestamp"];
const body = await readRawBody(req);
const signedPayload = `${timestamp}.${body}`;
const expected = crypto
.createHmac("sha256", process.env.AUTOREPORT_WEBHOOK_SECRET)
.update(signedPayload)
.digest("hex");
if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature))) {
return res.status(401).end("Invalid signature");
}Retries & idempotency
Respond with a 2xx within 10 seconds to acknowledge. Any non-2xx response (or a timeout) triggers our retry policy (up to 5 attempts over ~1 hour). Each attempt carries the same id so you can dedupe safely.