Reference

Webhook Events

Every event the engine emits, with field-by-field payload documentation. All webhooks are HMAC-signed, delivered with exponential-backoff retry, and audit-logged in webhook_deliveries so you can replay or diagnose any delivery.

Subscribe

Create a webhook subscription:

bash
curl -X POST http://localhost:8000/api/webhooks \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "label": "Clinician alerts",
    "url": "https://yourapp.example/webhook",
    "events": "proactive.*,safety.blocked"
  }'
The signing secret is shown once
The response includes signing_secret (awhsec_…string) — store it now. You'll use it to verify the X-Humane-Signature header on every incoming delivery.

Event filters

The events field takes a comma-separated list. Supported patterns:

  • * — everything
  • proactive.* — prefix wildcard
  • safety.blocked,safety.hold — explicit list

Verifying signatures

Every delivery carries X-Humane-Signature: t=<unix>,v1=<hex>. Compute HMAC-SHA256(secret, "{t}.{raw_body}") and compare constant-time. Reject timestamps older than 5 minutes (replay protection).

python
# Python — using the SDK's helper
from humane_ai import verify_webhook_signature

@app.post("/webhook")
async def receive(request: Request):
    raw = await request.body()
    sig = request.headers.get("X-Humane-Signature")
    if not verify_webhook_signature(MY_SIGNING_SECRET, raw, sig):
        raise HTTPException(401, "invalid signature")
    event = json.loads(raw)
    ...

Core events

interaction.processed

Every successful SDK /process call.

json
{
  "event": "interaction.processed",
  "timestamp": "2026-04-17T14:22:10Z",
  "data": {
    "user_id": "patient_42",
    "safety_action": "PROCEED",
    "mood": 0.48,
    "energy": 0.51,
    "interaction_count": 7
  }
}
user.created

First time an end_user appears for this tenant.

json
{
  "event": "user.created",
  "timestamp": "2026-04-17T14:22:10Z",
  "data": {
    "user_id": "patient_42",
    "channel": "ios_app",
    "timestamp": "2026-04-17T14:22:10Z"
  }
}
user.sentiment_shift

Sentiment changes by more than 0.2 in a single interaction.

json
{
  "event": "user.sentiment_shift",
  "timestamp": "2026-04-17T14:22:10Z",
  "data": {
    "user_id": "patient_42",
    "old_sentiment": 0.62,
    "new_sentiment": 0.31,
    "delta": 0.31,
    "direction": "negative"
  }
}

Safety events

safety.blocked

Pre-LLM safety gate returns BLOCK. The LLM is never called.

info
Always wire this up to a human channel. Slack / Discord integrations are the fastest path.
json
{
  "event": "safety.blocked",
  "timestamp": "2026-04-17T14:22:10Z",
  "data": {
    "user_id": "patient_42",
    "risk_score": 0.92,
    "flags": ["self_harm"],
    "message_preview": "I don't want to..."
  }
}
safety.hold

Ambiguous message — flagged for human review but not blocked.

json
{
  "event": "safety.hold",
  "timestamp": "2026-04-17T14:22:10Z",
  "data": {
    "user_id": "patient_42",
    "risk_score": 0.58,
    "flags": ["medication_advice"]
  }
}

Proactive events

proactive.mood_drop

End-user mood drops below 0.3.

json
{
  "event": "proactive.mood_drop",
  "timestamp": "2026-04-17T14:22:10Z",
  "data": {
    "trigger": "mood_drop",
    "description": "User mood dropped below critical threshold",
    "suggested_action": "Send empathetic check-in message",
    "end_user_id": "patient_42",
    "user_state": {
      "mood": 0.18, "energy": 0.42, "trust": 0.55,
      "interaction_count": 7,
      "last_seen": "2026-04-17T14:19:40Z"
    },
    "timestamp": "2026-04-17T14:22:10Z"
  }
}
proactive.energy_low

End-user energy drops below 0.25.

json
{
  "event": "proactive.energy_low",
  "timestamp": "...",
  "data": { "trigger": "energy_low", "end_user_id": "...", "user_state": { ... } }
}
proactive.trust_milestone

End-user crosses trust > 0.7 with more than 10 interactions.

json
{
  "event": "proactive.trust_milestone",
  "timestamp": "...",
  "data": { "trigger": "trust_milestone", "end_user_id": "...", "user_state": { ... } }
}
proactive.user_inactive

End-user has not interacted in more than 48 hours.

json
{
  "event": "proactive.user_inactive",
  "timestamp": "...",
  "data": { "trigger": "user_inactive", "end_user_id": "...", "user_state": { ... } }
}
proactive.policy_{rule_name}

Any policy with a fire_event action matched.

info
The suffix after 'proactive.' is your rule name. Subscribe with a wildcard: proactive.*
json
{
  "event": "proactive.policy_sustained_distress",
  "timestamp": "...",
  "data": {
    "user_id": "patient_42",
    "policy_rule": "sustained_distress",
    "mood": 0.22, "energy": 0.4, "trust": 0.55
  }
}

Audit trail

Every fired event is persisted in webhook_deliverieswhether or not a receiver was subscribed. Query them via:

bash
curl -H "X-API-Key: hx_..." \
  "http://localhost:8000/api/webhook-events?since_hours=168&limit=100"

Returns each event with delivery status and the full payload. Useful for the Signals UI, incident forensics, or a "re-deliver failed webhook" flow.