Everything Citesvue produces, signed, to your endpoint.
The generic webhook accepts every content type - findings, recaps, transcripts, timelines, reports, cited answers - as versioned JSON with an HMAC signature your receiver can verify. This page is the receiver contract.
Four steps to a working receiver.
- STEP 01
Stand up an HTTPS endpoint
Any public HTTPS URL that accepts POST and returns a 2xx quickly. Standard ports only (443 or 8443). Internal and private-network hosts are refused by design.
- STEP 02
Add the webhook in Citesvue
Settings, then Integrations under Push destinations, then the Webhook card. Enter the destination URL, a signing secret (recommended - it is what lets you authenticate us), and optional extra headers such as an Authorization value your endpoint expects. One webhook per workspace.
- STEP 03
Verify signatures in your receiver
Each delivery is signed with HMAC-SHA256 over the timestamp and the raw body. Recompute, compare in constant time, and reject stale timestamps. The exact recipe is below.
- STEP 04
Push something real
There is no synthetic test ping - a fake event would just complicate your handler. Push a finding or recap to the webhook from any recording and watch it arrive; Integration activity in Settings shows the delivery status alongside.
Headers and envelope.
Every delivery is a POST with a JSON body and these headers.
Content-Type: application/json X-Citesvue-Timestamp: <unix seconds> X-Citesvue-Idempotency-Key: <stable per delivery> X-Citesvue-Signature: sha256=<hex> # when a secret is set
The signature is HMAC-SHA256 over the string "{timestamp}.{raw body}", hex-encoded with a sha256= prefix.
{
"source": "citesvue",
"version": 1,
"event": {
"type": "artifact",
"deliveredAt": "2026-08-26T09:14:03Z",
"idempotencyKey": "…"
},
"account": { "id": "…" },
"recording": {
"id": "…",
"title": "UAT session - checkout flow",
"createdAt": "…",
"durationMs": 2400000,
"url": "https://…"
},
"data": { "artifact": { "id": "…", "type": "bug",
"title": "Checkout submits twice on slow connections",
"severity": "high", "evidenceQuote": "…",
"startMs": 863000, "speaker": "…", "url": "https://…" } }
}Verify the signature, correctly.
Three rules: use the raw bytes, compare in constant time, reject stale timestamps.
// Node.js - verify a Citesvue webhook delivery
const crypto = require('node:crypto');
function verify(req, rawBody, secret) {
const ts = req.headers['x-citesvue-timestamp'];
const sig = req.headers['x-citesvue-signature']; // "sha256=<hex>"
if (!ts || !sig) return false;
// Reject stale timestamps (replay protection)
if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(ts + '.' + rawBody) // the RAW bytes, not re-serialised JSON
.digest('hex');
return sig.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
}The common mistake is parsing the JSON and re-serialising it before hashing - key order and whitespace are part of what was signed, so always hash the raw request bytes exactly as received.
What arrives in data, by event.type.
One finding: type, title, description, severity, evidence quote, timestamps, speaker, and a link to the moment.
A set of findings in one delivery.
Overview, decisions, action items with owners, topics.
Speaker-attributed segments with start times; very long transcripts are marked truncated.
Moment-by-moment entries: label, time, speaker, detail.
The full report as titled sections.
A question, its answer, and the citations with quotes and timestamped links.
Four rules for a receiver that never loses an event.
- Answer fast, process later
Return 2xx as soon as the payload is stored. Any 2xx counts as delivered; slow handlers risk the delivery timing out and retrying.
- Dedupe on the idempotency key
X-Citesvue-Idempotency-Key is stable across retries of the same delivery. Store it and skip repeats; that makes your receiver exactly-once in practice.
- Expect retries
Failed deliveries retry automatically with growing backoff before being marked as given up in Integration activity, where a manual Retry remains available.
- Switch on event.type
The envelope is versioned and the data shape follows event.type. Ignore types you do not handle rather than erroring on them.
Delivery status, errors, and manual retry live in Settings under Integration activity - the same panel every destination uses. See troubleshooting for the state list.
Webhooks, answered.
- It is optional but strongly recommended: without it your receiver has no way to authenticate that a request came from Citesvue. The secret is used exactly as you typed it and is never sent to your endpoint.
- Destination URLs must be public HTTPS on port 443 or 8443, with no credentials embedded in the URL. Private addresses and internal hostnames are refused. Put authentication in the extra headers instead - an Authorization header is supported.
- Any 2xx within the delivery window. Non-2xx responses are classified: server errors and rate limits retry automatically; client errors mark the delivery failed with the reason shown in Integration activity.
- A workspace has one webhook, and it receives whatever you choose to push to it. Route by event.type inside your receiver - the envelope is designed for exactly that.
- The webhook is an integration: adding one works on every plan, pushing through it is included on paid plans, and viewers cannot push on any plan.
Your next recording could be
your most
valuable asset.
Or it could sit in a Drive folder nobody opens again. The difference is whether it has citations attached.
- SetupOne drag-and-drop upload, or send the notetaker. No plugins.
- First insightCited Q&A on a 60-min recording in under 6 minutes.
- Cancel anytimeFull data export, full right to erasure.