> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mnemom.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Safe House Webhooks

> Receive real-time HTTP POST notifications for Safe House evaluation events — block decisions, quarantine alerts, canary triggers, and campaign detection.

**React to threats in real-time.** Safe House 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.

Safe House webhooks reuse the existing AIP webhook infrastructure. If you already have AIP webhooks configured, add `sh.*` to your `event_types` — no new endpoints or secrets required.

```
Safe House evaluation ──→ Gateway ──→ Signed POST (X-AIP-Signature) ──→ Your Endpoint
                                            │
                                            ├── PagerDuty (sh.evaluation.block)
                                            ├── Slack (sh.evaluation.quarantine)
                                            ├── SIEM (all sh.*)
                                            └── Security on-call (sh.canary.triggered)
```

***

## Event types

| Event                      | When It Fires                                                              | Severity      |
| -------------------------- | -------------------------------------------------------------------------- | ------------- |
| `sh.evaluation.warn`       | Safe House flags a turn above the warn threshold but below block           | Informational |
| `sh.evaluation.quarantine` | Safe House quarantines a turn for human review                             | High          |
| `sh.evaluation.block`      | Safe House blocks a turn outright                                          | Critical      |
| `sh.canary.triggered`      | A canary credential was used — indicating active exploitation              | Critical      |
| `sh.session.escalated`     | Session risk has risen to `high` across multiple turns                     | High          |
| `sh.campaign.detected`     | Multiple agents have received similar attacks from the same infrastructure | Critical      |

***

## Payload structure

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

### `sh.evaluation.block`

```json theme={null}
{
  "event": "sh.evaluation.block",
  "timestamp": "2026-03-30T14:32:11Z",
  "agent_id": "mnm-550e8400-e29b-41d4-a716-446655440000",
  "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://mnemom.ai/dashboard/agents/mnm-550e8400-e29b-41d4-a716-446655440000"
  }
}
```

### `sh.evaluation.quarantine`

```json theme={null}
{
  "event": "sh.evaluation.quarantine",
  "timestamp": "2026-03-30T15:11:04Z",
  "agent_id": "mnm-550e8400-e29b-41d4-a716-446655440000",
  "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://mnemom.ai/dashboard/agents/mnm-550e8400-e29b-41d4-a716-446655440000"
  }
}
```

### `sh.canary.triggered`

```json theme={null}
{
  "event": "sh.canary.triggered",
  "timestamp": "2026-03-30T16:00:00Z",
  "agent_id": "mnm-550e8400-e29b-41d4-a716-446655440000",
  "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"
  }
}
```

### `sh.session.escalated`

```json theme={null}
{
  "event": "sh.session.escalated",
  "timestamp": "2026-03-30T16:45:22Z",
  "agent_id": "mnm-0b3f2a1c-d4e5-4f60-b7a8-9c0d1e2f3a4b",
  "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"
  }
}
```

### `sh.campaign.detected`

```json theme={null}
{
  "event": "sh.campaign.detected",
  "timestamp": "2026-03-30T17:22:09Z",
  "data": {
    "campaign_id": "camp_b3c9d4a1",
    "threat_type": "bec_fraud",
    "affected_agents": ["mnm-550e8400-e29b-41d4-a716-446655440000", "mnm-0b3f2a1c-d4e5-4f60-b7a8-9c0d1e2f3a4b", "mnm-a4bc5d6e-7f89-4012-b3c4-d5e6f7a8b9c0"],
    "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 Safe House 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.

```typescript theme={null}
import crypto from 'crypto';
import { Request, Response } from 'express';

export function handleSafeHouseWebhook(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
}
```

<Warning>
  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.
</Warning>

**Timestamp freshness check (recommended):** Reject payloads where `timestamp` is older than 5 minutes to prevent replay attacks.

```typescript theme={null}
const age = Date.now() - new Date(event.timestamp).getTime();
if (age > 5 * 60 * 1000) {
  return res.status(400).send('Stale event');
}
```

***

## Registration

Safe House events use the same webhook registration as AIP webhooks. Add `sh.*` to your `event_types`:

```bash theme={null}
# Add Safe House 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",
      "sideband.drift.fired",
      "sh.evaluation.block",
      "sh.evaluation.quarantine",
      "sh.evaluation.warn",
      "sh.canary.triggered",
      "sh.session.escalated",
      "sh.campaign.detected"
    ]
  }'
```

Or register a dedicated Safe House endpoint:

```bash theme={null}
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/safe-house",
    "description": "Safe House security alerts",
    "event_types": [
      "sh.evaluation.block",
      "sh.evaluation.quarantine",
      "sh.canary.triggered",
      "sh.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:

| Attempt     | Delay     |
| ----------- | --------- |
| 1 (initial) | Immediate |
| 2           | 1 000 ms  |
| 3           | 5 000 ms  |
| 4           | 15 000 ms |

After 4 failed attempts, the delivery is marked `failed` in `aip_webhook_deliveries`. You can redeliver manually:

```bash theme={null}
curl -X POST https://api.mnemom.ai/v1/orgs/{org_id}/webhooks/deliveries/{delivery_id}/redeliver \
  -H "Authorization: Bearer $TOKEN"
```

***

## Integration patterns

### Route `sh.evaluation.block` to on-call

High-confidence blocks warrant immediate human attention. Page your security on-call directly.

```typescript theme={null}
if (event.event === 'sh.evaluation.block' && event.data.overall_risk > 0.9) {
  await pagerduty.createIncident({
    title: `Safe House 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 `sh.evaluation.quarantine` to human reviewer

Quarantined items need human review before the session can continue. Post to Slack with the review link.

```typescript theme={null}
if (event.event === 'sh.evaluation.quarantine') {
  await slack.postMessage({
    channel: '#security-review',
    text: [
      `*Safe House 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 `sh.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.

```typescript theme={null}
if (event.event === 'sh.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 Safe House events to your SIEM

```typescript theme={null}
app.post('/webhooks/safe-house', verifySignature, (req, res) => {
  const event = req.body;
  siem.ingest({
    source: 'mnemom-safe-house',
    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:

```bash theme={null}
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": "sh.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.

***

## See also

* [Webhook Notifications](/guides/webhooks) — Full webhook infrastructure documentation
* [Safe House Threat Model](/guides/safe-house-threat-model) — What each threat type means and how it's detected
* [Safe House Monitoring](/guides/safe-house-monitoring) — Query evaluation history, metrics, and the security observatory
* [Safe House API Reference](/api-reference/safe-house-overview) — Quarantine management and configuration endpoints
