Back to Docs
Webhooks

Webhooks

GroundTruth can notify your server every time a verification check finishes. You provide a URL, and we send an HTTP POST request to it with the result. No polling required.

How it works

  1. Add your URL — Go to Settings and paste the endpoint where you want to receive events (e.g. https://yourapp.com/webhooks/groundtruth).
  2. We send events — When a check completes, we POST a JSON payload to your URL with the result.
  3. Your server responds — Return any 2xx status to acknowledge receipt. That's it.

Event payload: check.completed

This is what GroundTruth sends to your endpoint after each verification check.

json
{
  "id": "evt_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "type": "check.completed",
  "created_at": "2025-01-15T14:32:01Z",
  "data": {
    "check_id": "chk_abc123",
    "risk_score": 0.72,
    "verdict": "high_risk",
    "claims_count": 3,
    "has_rewrite": true
  }
}

Event payload: execution.approved / execution.rejected

Fired when a human reviews an escalated action in the approvals workflow. Use these events to notify agent orchestrators or resume paused workflows without polling.

json
{
  "id": "evt_f7e8d9c0-b1a2-3456-cdef-ab9876543210",
  "type": "execution.approved",
  "created_at": "2025-03-15T15:05:00Z",
  "data": {
    "executionId": "exec_escalated456",
    "action": "reply_ticket",
    "decision": "approved",
    "reviewedBy": "admin@example.com",
    "sessionId": "sess_abc123"
  }
}

The type field is execution.approved or execution.rejected depending on the reviewer's decision. sessionId is included when available, so you can correlate with the originating agent session.

Headers included in every delivery

These headers are sent by GroundTruth with each webhook request.

HeaderDescription
X-Webhook-IdUnique ID for this delivery. Use it for idempotency to avoid processing the same event twice.
X-Webhook-EventThe event type: check.completed, execution.approved, or execution.rejected.
X-Webhook-SignatureHMAC-SHA256 signature of the request body: sha256=<hex>. Use this to verify the request is genuinely from GroundTruth.
User-AgentAlways GroundTruth/1.0.

Verifying signatures (recommended)

To confirm a webhook was sent by GroundTruth and not a third party, verify the X-Webhook-Signature header against your signing secret (found in Settings).

node.js
import { createHmac, timingSafeEqual } from "crypto";

function isValidSignature(rawBody, signatureHeader, secret) {
  const expected = "sha256=" +
    createHmac("sha256", secret).update(rawBody).digest("hex");
  return timingSafeEqual(
    Buffer.from(signatureHeader),
    Buffer.from(expected)
  );
}

// Example: Express route handling the webhook
app.post("/webhooks/groundtruth", (req, res) => {
  const signature = req.headers["x-webhook-signature"];

  if (!isValidSignature(req.rawBody, signature, process.env.WEBHOOK_SECRET)) {
    return res.status(401).send("Invalid signature");
  }

  // Signature is valid — process the event
  const event = JSON.parse(req.rawBody);
  console.log(event.type, event.data.risk_score);

  res.sendStatus(200);
});

Retry policy

If your endpoint returns a non-2xx status or does not respond within 10 seconds, we retry once after a 2-second delay. If the retry also fails, the delivery is dropped. Make sure your endpoint is available and responds quickly to avoid missed events.