Skip to main content

CFD Webhooks

React to threats in real-time. CFD emits webhook events when it blocks a message, quarantines a session, detects a canary trigger, or identifies a cross-agent attack campaign. Route these events to PagerDuty, Slack, your SIEM, or any HTTP endpoint. CFD webhooks reuse the existing AIP webhook infrastructure. If you already have AIP webhooks configured, add cfd.* to your event_types — no new endpoints or secrets required.
CFD evaluation ──→ Gateway ──→ Signed POST (X-AIP-Signature) ──→ Your Endpoint

                                      ├── PagerDuty (cfd.evaluation.block)
                                      ├── Slack (cfd.evaluation.quarantine)
                                      ├── SIEM (all cfd.*)
                                      └── Security on-call (cfd.canary.triggered)

Event Types

EventWhen It FiresSeverity
cfd.evaluation.warnCFD flags a turn above the warn threshold but below blockInformational
cfd.evaluation.quarantineCFD quarantines a turn for human reviewHigh
cfd.evaluation.blockCFD blocks a turn outrightCritical
cfd.canary.triggeredA canary credential was used — indicating active exploitationCritical
cfd.session.escalatedSession risk has risen to high across multiple turnsHigh
cfd.campaign.detectedMultiple agents have received similar attacks from the same infrastructureCritical

Payload Structure

All events share a common envelope. The data object varies by event type.

cfd.evaluation.block

{
  "event": "cfd.evaluation.block",
  "timestamp": "2026-03-30T14:32:11Z",
  "agent_id": "smolt-18a228f3",
  "session_id": "18a228f3-1234",
  "data": {
    "verdict": "block",
    "quarantine_id": "qid_7f3a9b2c",
    "overall_risk": 0.94,
    "top_threat": {
      "type": "bec_fraud",
      "confidence": 0.94,
      "reasoning": "Financial action ('wire transfer') · Urgency ('immediately') · Authority ('CEO') · Secrecy ('don't tell')"
    },
    "detection_layer": "l2",
    "review_url": "https://app.mnemom.com/cfd/quarantine/qid_7f3a9b2c"
  }
}

cfd.evaluation.quarantine

{
  "event": "cfd.evaluation.quarantine",
  "timestamp": "2026-03-30T15:11:04Z",
  "agent_id": "smolt-18a228f3",
  "session_id": "18a228f3-1234",
  "data": {
    "verdict": "quarantine",
    "quarantine_id": "qid_2a9c4f11",
    "overall_risk": 0.71,
    "top_threat": {
      "type": "indirect_injection",
      "confidence": 0.71,
      "reasoning": "Imperative instruction structure detected in tool result · References external data destination"
    },
    "detection_layer": "l1",
    "pending_human_review": true,
    "review_url": "https://app.mnemom.com/cfd/quarantine/qid_2a9c4f11"
  }
}

cfd.canary.triggered

{
  "event": "cfd.canary.triggered",
  "timestamp": "2026-03-30T16:00:00Z",
  "agent_id": "smolt-18a228f3",
  "session_id": "18a228f3-5678",
  "data": {
    "canary_id": "can_f9e2a01b",
    "canary_type": "api_key",
    "triggered_by": "inbound_message",
    "raw_signal": "Canary credential appeared in inbound turn content",
    "action_taken": "session_terminated"
  }
}

cfd.session.escalated

{
  "event": "cfd.session.escalated",
  "timestamp": "2026-03-30T16:45:22Z",
  "agent_id": "smolt-a9f34c12",
  "session_id": "a9f34c12-2233",
  "data": {
    "previous_risk": "medium",
    "current_risk": "high",
    "escalation_reason": "hijack_attempt",
    "turn_count": 7,
    "pattern_description": "Benign session pivot — topic and action scope shifted abruptly at turn 6"
  }
}

cfd.campaign.detected

{
  "event": "cfd.campaign.detected",
  "timestamp": "2026-03-30T17:22:09Z",
  "data": {
    "campaign_id": "camp_b3c9d4a1",
    "threat_type": "bec_fraud",
    "affected_agents": ["smolt-18a228f3", "smolt-a9f34c12", "smolt-00ff1122"],
    "agent_count": 3,
    "similarity_score": 0.92,
    "first_seen": "2026-03-30T16:50:00Z",
    "infrastructure_hint": "Similar sender fingerprint and payload structure across agents"
  }
}

Signature Verification

Every CFD webhook is signed with HMAC-SHA256, identical to AIP webhooks. The signature header is X-AIP-Signature with value sha256=<hex digest>. Always verify signatures before acting on a webhook. An unverified webhook can be spoofed.
import crypto from 'crypto';
import { Request, Response } from 'express';

export function handleCfdWebhook(req: Request, res: Response) {
  const signature = req.headers['x-aip-signature'] as string;
  const secret = process.env.MNEMOM_WEBHOOK_SECRET!;

  // Use the raw body — any middleware that parses JSON changes the byte sequence
  const body = req.rawBody; // or however your framework exposes raw bytes

  const expected =
    'sha256=' +
    crypto.createHmac('sha256', secret).update(body).digest('hex');

  if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
    return res.status(401).send('Invalid signature');
  }

  const event = JSON.parse(body);
  // safe to process
}
Use timingSafeEqual rather than === for signature comparison. String equality is vulnerable to timing attacks that can be used to brute-force the secret one byte at a time.
Timestamp freshness check (recommended): Reject payloads where timestamp is older than 5 minutes to prevent replay attacks.
const age = Date.now() - new Date(event.timestamp).getTime();
if (age > 5 * 60 * 1000) {
  return res.status(400).send('Stale event');
}

Registration

CFD events use the same webhook registration as AIP webhooks. Add cfd.* to your event_types:
# Add CFD events to an existing webhook endpoint
curl -X PATCH https://api.mnemom.ai/v1/orgs/{org_id}/webhooks/{endpoint_id} \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "event_types": [
      "integrity.violation",
      "drift.detected",
      "cfd.evaluation.block",
      "cfd.evaluation.quarantine",
      "cfd.evaluation.warn",
      "cfd.canary.triggered",
      "cfd.session.escalated",
      "cfd.campaign.detected"
    ]
  }'
Or register a dedicated CFD endpoint:
curl -X POST https://api.mnemom.ai/v1/orgs/{org_id}/webhooks \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-server.com/webhooks/cfd",
    "description": "CFD security alerts",
    "event_types": [
      "cfd.evaluation.block",
      "cfd.evaluation.quarantine",
      "cfd.canary.triggered",
      "cfd.campaign.detected"
    ]
  }'

Retry Logic

Webhook delivery follows the same retry policy as AIP webhooks. If your endpoint returns a non-2xx status, Mnemom retries at increasing intervals:
AttemptDelay
1 (initial)Immediate
21 000 ms
35 000 ms
415 000 ms
After 4 failed attempts, the delivery is marked failed in aip_webhook_deliveries. You can redeliver manually:
curl -X POST https://api.mnemom.ai/v1/orgs/{org_id}/webhooks/deliveries/{delivery_id}/redeliver \
  -H "Authorization: Bearer $TOKEN"

Integration Patterns

Route cfd.evaluation.block to on-call

High-confidence blocks warrant immediate human attention. Page your security on-call directly.
if (event.event === 'cfd.evaluation.block' && event.data.overall_risk > 0.9) {
  await pagerduty.createIncident({
    title: `CFD block: ${event.data.top_threat.type} on agent ${event.agent_id}`,
    severity: 'critical',
    body: event.data.top_threat.reasoning,
    links: [{ href: event.data.review_url, text: 'Review in Mnemom' }],
  });
}

Route cfd.evaluation.quarantine to human reviewer

Quarantined items need human review before the session can continue. Post to Slack with the review link.
if (event.event === 'cfd.evaluation.quarantine') {
  await slack.postMessage({
    channel: '#security-review',
    text: [
      `*CFD Quarantine* — Agent \`${event.agent_id}\``,
      `Threat: \`${event.data.top_threat.type}\` (${Math.round(event.data.top_threat.confidence * 100)}% confidence)`,
      `Reasoning: ${event.data.top_threat.reasoning}`,
      `<${event.data.review_url}|Review and release →>`,
    ].join('\n'),
  });
}

Treat cfd.canary.triggered as a security incident

A canary trigger means an attacker has gained access to a credential planted specifically to detect exploitation. Escalate immediately.
if (event.event === 'cfd.canary.triggered') {
  await incidentResponse.openP0({
    title: `Canary credential triggered — active exploitation suspected`,
    agentId: event.agent_id,
    sessionId: event.session_id,
    canaryId: event.data.canary_id,
  });

  // Optionally: contain the agent immediately
  await fetch(`https://api.mnemom.ai/v1/orgs/${ORG_ID}/agents/${event.agent_id}/pause`, {
    method: 'POST',
    headers: { Authorization: `Bearer ${process.env.MNEMOM_API_KEY}` },
    body: JSON.stringify({ reason: 'Canary credential triggered — automated containment' }),
  });
}

Log all CFD events to your SIEM

app.post('/webhooks/cfd', verifySignature, (req, res) => {
  const event = req.body;
  siem.ingest({
    source: 'mnemom-cfd',
    event_type: event.event,
    agent_id: event.agent_id,
    session_id: event.session_id,
    timestamp: event.timestamp,
    payload: event.data,
  });
  res.status(200).send('ok');
});

Testing Your Integration

Send a test event to your endpoint without waiting for a real threat:
curl -X POST https://api.mnemom.ai/v1/orgs/{org_id}/webhooks/{endpoint_id}/test \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"event_type": "cfd.evaluation.block"}'
The test payload uses synthetic data but is signed with your real signing secret — your signature verification code runs against a real payload.