Webhooks

React to ingestion, entity and quality events in your own systems.

Register an HTTPS endpoint and Chordian will post events to it as they happen.

Events

EventFires when
memory.ingest.completedA document finished indexing
memory.ingest.dlp_flaggedThe perimeter masked something during ingest
memory.ingest.auto_routedContent was classified — includes needs_review
memory.entity.createdA new entity entered the graph
memory.entity.contradiction_detectedTwo sources disagreed
memory.entity.contradiction_resolvedSomeone resolved a disagreement
memory.health.assessedA health score was computed

Delivery

Deliveries are signed with HMAC-SHA256 and retried on failure at 1s, 2s, 4s, 8s and 30s. After five failures the delivery is dead-lettered rather than dropped, so an outage on your side is recoverable rather than silent data loss.

Verify every delivery

An unverified webhook endpoint is an unauthenticated write to your system.

verify.ts
import { createHmac, timingSafeEqual } from 'node:crypto';

export function verify(rawBody: string, signature: string, secret: string) {
  const expected = createHmac('sha256', secret).update(rawBody).digest('hex');
  const a = Buffer.from(expected);
  const b = Buffer.from(signature);
  // Length check first: timingSafeEqual throws on a length mismatch.
  return a.length === b.length && timingSafeEqual(a, b);
}

Two details that are easy to get wrong:

  • Verify against the raw body, before any JSON parsing. Re-serialising changes the bytes and the signature will not match.
  • Compare in constant time. A plain === leaks how much of the signature was correct, one request at a time.

Be idempotent

Retries mean your endpoint will occasionally see the same event twice. Key on the event id and make reprocessing a no-op — this is cheaper to build now than to debug later.

Respond fast

Acknowledge with 2xx immediately and do the work asynchronously. A slow endpoint gets treated as a failing one, which turns your latency problem into a retry storm.

Was this page helpful?
Report an issue

On this page