Fact Checking Webhooks
Receive notifications when fact-check operations complete or change status.
Event Types
| Event | When it fires | Page |
|---|---|---|
fact_check.completed | A fact-check finishes successfully with totals | fact_check.completed |
fact_check.status_changed | A fact-check transitions between statuses | fact_check.status_changed |
fact_check.failed | Reserved — not currently emitted | fact_check.failed |
See the per-event-type pages for the authoritative data schemas. The sections below cover the fact-checking-specific behavior.
When Webhooks Send
fact_check.completed— fires once when processing finishes. Payload reports counts (totalClaims,verifiedClaims) andprocessingTimeMs. Per-claim verdicts are not in the webhook — fetch them from the API usingfactCheckId.fact_check.status_changed— fires on every status transition during the workflow (e.g.pending→processing→completed). When the new status is terminal,finalDeterminationandfinalConfidenceare populated; otherwise they arenull.fact_check.failed— reserved for future use. The schema is published so you can build against it, but the platform does not emit this event today.
How Webhooks Send
All fact-checking webhooks follow the standard Helix webhook protocol — see the Webhook Overview for envelope shape, security verification, retry policy, and HTTP headers.
Example Handler
import type { Request, Response } from 'express';
app.post('/webhooks/fact-check', async (req: Request, res: Response) => {
// 1. Verify signature against the RAW body (see Webhook Overview).
// 2. Acknowledge receipt immediately. Process async.
res.status(200).send('OK');
// 3. Use envelope `id` for idempotency — stable across retries.
const { eventType, id, data } = req.body;
if (await alreadyProcessed(id)) return;
try {
switch (eventType) {
case 'fact_check.completed': {
// data: { factCheckId, status, totalClaims, verifiedClaims, processingTimeMs }
const factCheck = await helixApi.getFactCheck(data.factCheckId);
await processFactCheck(factCheck);
break;
}
case 'fact_check.status_changed': {
// data: { factCheckId, status, finalDetermination?, finalConfidence?, completedAt? }
await updateFactCheckStatus(data.factCheckId, data.status);
if (data.finalDetermination) {
await recordVerdict(
data.factCheckId,
data.finalDetermination,
data.finalConfidence
);
}
break;
}
case 'fact_check.failed': {
// Reserved — not emitted today. Safe to leave a no-op stub.
break;
}
}
await markProcessed(id);
} catch (err) {
// Already 200'd — log, don't rethrow. Failed sync is recoverable
// on the next webhook or a periodic full reconcile.
console.error('Webhook processing failed', { id, eventType, err });
}
});
Webhooks are signals, not the source of truth
Fact-check webhooks tell you something changed. For per-claim verdicts, sources, and full reasoning, fetch the fact-check from the API using data.factCheckId. See Webhook Overview.