Skip to main content

Fact Checking Webhooks

Receive notifications when fact-check operations complete or change status.

Event Types

EventWhen it firesPage
fact_check.completedA fact-check finishes successfully with totalsfact_check.completed
fact_check.status_changedA fact-check transitions between statusesfact_check.status_changed
fact_check.failedReserved — not currently emittedfact_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) and processingTimeMs. Per-claim verdicts are not in the webhook — fetch them from the API using factCheckId.
  • fact_check.status_changed — fires on every status transition during the workflow (e.g. pendingprocessingcompleted). When the new status is terminal, finalDetermination and finalConfidence are populated; otherwise they are null.
  • 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.