Your AI agent just learned something important about a customer. Maybe it extracted a new entity from a conversation. Maybe it detected a contradiction with something it remembered last week. Maybe a critical memory just crossed the decay threshold and is about to expire.

How would you know?

Without webhooks, you wouldn't — not until your agent made a mistake. With 0Latency's webhook system, you get real-time notifications for every meaningful memory event. This guide walks you through setting them up, the event types available, and practical integration patterns for Slack, logging, and audit trails.

Why Memory Webhooks Matter

Most memory systems are black boxes. Data goes in, data comes out, and you hope the middle part works correctly. That's fine for prototyping. It's not fine for production.

In production, you need to know:

  • What your agent is learning — is it extracting the right entities? Building correct relationships?
  • When memories conflict — a customer said they use React last month but mentioned Vue today. Which is current?
  • When critical context expires — a support ticket's context is decaying. Should someone follow up?
  • Compliance and audit trails — regulated industries need to track what an agent knows and when it learned it.

Webhooks turn your memory layer from a black box into an observable system. They're the difference between "I think the agent is working" and "I know exactly what the agent knows."

Registering a Webhook

Setting up a webhook takes a single API call. You specify a URL, the events you care about, and optionally filter by agent.

Register a webhook
curl -X POST https://api.0latency.ai/docs (webhook endpoints - see API docs) \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-app.com/webhooks/memory",
    "events": [
      "memory.extraction",
      "memory.contradiction",
      "memory.decay_threshold"
    ],
    "agent_id": "support-bot",
    "secret": "whsec_your_signing_secret"
  }'

Response:

{
  "id": "wh_abc123",
  "url": "https://your-app.com/webhooks/memory",
  "events": ["memory.extraction", "memory.contradiction", "memory.decay_threshold"],
  "agent_id": "support-bot",
  "status": "active",
  "created_at": "2026-03-30T12:00:00Z"
}

Security: Every webhook delivery includes an X-0Latency-Signature header — an HMAC-SHA256 hash of the payload using your signing secret. Always verify this before processing events.

Event Types

0Latency fires webhooks for five categories of memory events. Each delivers a structured JSON payload with full context.

memory.extraction Entity Extraction

Fired when the memory engine extracts entities, relationships, or structured data from a /store call. Tells you exactly what your agent learned.

{
  "event": "memory.extraction",
  "timestamp": "2026-03-30T14:23:01Z",
  "agent_id": "support-bot",
  "memory_id": "mem_xyz789",
  "data": {
    "entities": [
      { "name": "Sarah Chen", "type": "person", "confidence": 0.94 },
      { "name": "Acme Corp", "type": "organization", "confidence": 0.98 }
    ],
    "relationships": [
      { "from": "Sarah Chen", "relation": "works_at", "to": "Acme Corp", "confidence": 0.91 }
    ],
    "sentiment": { "overall": "positive", "score": 0.72 }
  }
}

memory.contradiction Contradiction Detected

Fired when new information conflicts with existing memories. Critical for maintaining data accuracy and flagging situations that may need human review.

{
  "event": "memory.contradiction",
  "timestamp": "2026-03-30T15:10:44Z",
  "agent_id": "support-bot",
  "data": {
    "new_memory": {
      "id": "mem_new456",
      "content": "Customer uses Vue.js for their frontend"
    },
    "conflicting_memory": {
      "id": "mem_old123",
      "content": "Customer uses React for their frontend",
      "stored_at": "2026-02-15T09:30:00Z"
    },
    "resolution": "newer_preferred",
    "confidence_delta": -0.23,
    "requires_review": false
  }
}

memory.decay_threshold Decay Threshold

Fired when a memory's confidence score drops below a configured threshold. Useful for triggering follow-ups or re-confirmation workflows.

{
  "event": "memory.decay_threshold",
  "timestamp": "2026-03-30T16:00:00Z",
  "agent_id": "support-bot",
  "data": {
    "memory_id": "mem_old789",
    "content": "Customer's contract renews in Q2 2026",
    "current_confidence": 0.32,
    "threshold": 0.40,
    "last_reinforced": "2026-01-10T11:00:00Z",
    "days_since_reinforcement": 79
  }
}

memory.relationship Relationship Created

Fired when the graph memory engine creates or updates a relationship between entities. Track how your agent's knowledge graph evolves.

{
  "event": "memory.relationship",
  "timestamp": "2026-03-30T14:23:02Z",
  "agent_id": "support-bot",
  "data": {
    "from": { "name": "Sarah Chen", "type": "person" },
    "relation": "manages",
    "to": { "name": "Project Atlas", "type": "project" },
    "confidence": 0.87,
    "source_memory": "mem_xyz789",
    "is_new": true
  }
}

memory.sentiment_shift Sentiment Shift

Fired when an entity's aggregated sentiment changes significantly. Detect when a customer is becoming frustrated or a project is trending negative.

{
  "event": "memory.sentiment_shift",
  "timestamp": "2026-03-30T17:45:00Z",
  "agent_id": "support-bot",
  "data": {
    "entity": "Acme Corp",
    "previous_sentiment": { "label": "positive", "score": 0.68 },
    "current_sentiment": { "label": "negative", "score": -0.41 },
    "shift_magnitude": 1.09,
    "contributing_memories": ["mem_a1", "mem_a2", "mem_a3"],
    "window": "7_days"
  }
}

Verifying Webhook Signatures

Every webhook delivery is signed. Here's how to verify it in your handler:

Node.js — Express middleware
const crypto = require('crypto');

function verifyWebhook(req, res, next) {
  const signature = req.headers['x-0latency-signature'];
  const timestamp = req.headers['x-0latency-timestamp'];
  const body = JSON.stringify(req.body);

  // Reject if timestamp is older than 5 minutes (replay protection)
  const age = Date.now() - new Date(timestamp).getTime();
  if (age > 300000) {
    return res.status(401).json({ error: 'Timestamp too old' });
  }

  const expected = crypto
    .createHmac('sha256', process.env.WEBHOOK_SECRET)
    .update(`${timestamp}.${body}`)
    .digest('hex');

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

  next();
}

app.post('/webhooks/memory', verifyWebhook, (req, res) => {
  // Process the event
  handleMemoryEvent(req.body);
  res.status(200).json({ received: true });
});
Python — Flask
import hmac, hashlib, time
from flask import Flask, request, jsonify

app = Flask(__name__)
WEBHOOK_SECRET = os.environ['WEBHOOK_SECRET']

@app.route('/webhooks/memory', methods=['POST'])
def handle_webhook():
    signature = request.headers.get('X-0Latency-Signature')
    timestamp = request.headers.get('X-0Latency-Timestamp')
    body = request.get_data(as_text=True)

    # Verify timestamp freshness
    ts = datetime.fromisoformat(timestamp.replace('Z', '+00:00'))
    if (datetime.now(timezone.utc) - ts).total_seconds() > 300:
        return jsonify(error='Timestamp too old'), 401

    # Verify signature
    expected = hmac.new(
        WEBHOOK_SECRET.encode(),
        f"{timestamp}.{body}".encode(),
        hashlib.sha256
    ).hexdigest()

    if not hmac.compare_digest(signature, expected):
        return jsonify(error='Invalid signature'), 401

    event = request.json
    handle_memory_event(event)
    return jsonify(received=True), 200

Integration Pattern: Slack Alerts

One of the most common webhook integrations: send memory events to a Slack channel so your team can monitor what your agent is learning in real time.

Slack integration — Node.js
const SLACK_WEBHOOK = process.env.SLACK_WEBHOOK_URL;

async function handleMemoryEvent(event) {
  const blocks = [];

  switch (event.event) {
    case 'memory.contradiction':
      blocks.push({
        type: 'section',
        text: {
          type: 'mrkdwn',
          text: `⚠️ *Contradiction Detected* — \`${event.agent_id}\`\n` +
            `> New: ${event.data.new_memory.content}\n` +
            `> Old: ${event.data.conflicting_memory.content}\n` +
            `Resolution: \`${event.data.resolution}\``
        }
      });
      break;

    case 'memory.decay_threshold':
      blocks.push({
        type: 'section',
        text: {
          type: 'mrkdwn',
          text: `🕐 *Memory Decaying* — \`${event.agent_id}\`\n` +
            `> ${event.data.content}\n` +
            `Confidence: ${(event.data.current_confidence * 100).toFixed(0)}% ` +
            `(${event.data.days_since_reinforcement} days stale)`
        }
      });
      break;

    case 'memory.sentiment_shift':
      const emoji = event.data.current_sentiment.score > 0 ? '📈' : '📉';
      blocks.push({
        type: 'section',
        text: {
          type: 'mrkdwn',
          text: `${emoji} *Sentiment Shift* — ${event.data.entity}\n` +
            `${event.data.previous_sentiment.label} → ` +
            `*${event.data.current_sentiment.label}* ` +
            `(magnitude: ${event.data.shift_magnitude.toFixed(2)})`
        }
      });
      break;
  }

  if (blocks.length > 0) {
    await fetch(SLACK_WEBHOOK, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ blocks })
    });
  }
}

This gives your team a live feed of the most important memory events — contradictions that might need human review, memories that are going stale, and sentiment shifts that could indicate customer churn risk.

Integration Pattern: Structured Logging

For production observability, pipe memory events into your existing logging infrastructure. Here's how to send events to a structured logging service:

Structured logging with Datadog
const { createLogger, format, transports } = require('winston');

const logger = createLogger({
  format: format.combine(format.timestamp(), format.json()),
  transports: [
    new transports.Http({
      host: 'http-intake.logs.datadoghq.com',
      path: `/api/v2/logs?dd-api-key=${process.env.DD_API_KEY}`,
      ssl: true
    })
  ]
});

async function handleMemoryEvent(event) {
  logger.info('memory_event', {
    event_type: event.event,
    agent_id: event.agent_id,
    timestamp: event.timestamp,
    memory_id: event.data.memory_id || null,
    entities_count: event.data.entities?.length || 0,
    relationships_count: event.data.relationships?.length || 0,
    confidence: event.data.current_confidence || null,
    sentiment: event.data.current_sentiment?.label || null,
    // Custom tags for Datadog dashboards
    ddtags: `agent:${event.agent_id},event:${event.event}`
  });
}

With structured logs, you can build dashboards tracking extraction rates, contradiction frequency, average memory confidence, and sentiment trends — all the metrics you need to know your agent's memory is healthy.

Integration Pattern: Audit Trails

For compliance-heavy industries (healthcare, finance, legal), you need an immutable record of everything your agent knows and when it learned it. Webhooks handle this directly:

Audit trail — PostgreSQL
// Store every memory event for compliance
async function handleMemoryEvent(event) {
  await pool.query(`
    INSERT INTO memory_audit_log (
      event_type, agent_id, event_timestamp,
      memory_id, payload, received_at
    ) VALUES ($1, $2, $3, $4, $5, NOW())
  `, [
    event.event,
    event.agent_id,
    event.timestamp,
    event.data.memory_id || event.data.new_memory?.id || null,
    JSON.stringify(event.data)
  ]);

  // Flag contradictions for human review
  if (event.event === 'memory.contradiction') {
    await pool.query(`
      INSERT INTO contradiction_reviews (
        agent_id, new_memory_id, old_memory_id,
        resolution, status, created_at
      ) VALUES ($1, $2, $3, $4, 'pending', NOW())
    `, [
      event.agent_id,
      event.data.new_memory.id,
      event.data.conflicting_memory.id,
      event.data.resolution
    ]);
  }
}

This gives you a complete, queryable history of your agent's learning process — essential for audits, debugging, and understanding how your agent's knowledge evolved over time.

Managing Webhooks

The full webhook management API lets you list, update, test, and delete webhooks:

# List all webhooks
curl https://api.0latency.ai/docs (webhook endpoints - see API docs) \
  -H "X-API-Key: YOUR_API_KEY"

# Test a webhook (sends a test event)
curl -X POST https://api.0latency.ai/docs (webhook endpoints - see API docs)/wh_abc123/test \
  -H "X-API-Key: YOUR_API_KEY"

# Update events or URL
curl -X PATCH https://api.0latency.ai/docs (webhook endpoints - see API docs)/wh_abc123 \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "events": ["memory.contradiction"] }'

# Pause a webhook
curl -X PATCH https://api.0latency.ai/docs (webhook endpoints - see API docs)/wh_abc123 \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "status": "paused" }'

# Delete a webhook
curl -X DELETE https://api.0latency.ai/docs (webhook endpoints - see API docs)/wh_abc123 \
  -H "X-API-Key: YOUR_API_KEY"

Delivery Guarantees and Retry Logic

0Latency webhooks use at-least-once delivery with exponential backoff:

  • First attempt: Immediate on event trigger
  • Retry 1: 30 seconds
  • Retry 2: 2 minutes
  • Retry 3: 15 minutes
  • Retry 4: 1 hour
  • Retry 5: 4 hours (final attempt)

A delivery is considered successful on any 2xx response. If all retries fail, the event is logged and available via the events API for manual retrieval.

Idempotency: Every webhook payload includes a unique event_id. Use this to deduplicate in your handler, since at-least-once delivery means you may receive the same event twice during retries.

Best Practices

  1. Respond fast. Return a 200 immediately, then process asynchronously. If your handler takes more than 10 seconds, the delivery will time out and retry.
  2. Always verify signatures. Never process unsigned payloads. This protects against spoofed events.
  3. Filter events at registration. Only subscribe to events you'll act on. Less noise means fewer wasted compute cycles.
  4. Use the test endpoint. Before deploying, send test events to verify your handler works correctly.
  5. Monitor your endpoint. If 0Latency can't reach your URL, events queue up. Set up uptime monitoring on your webhook receiver.

For more on building observable AI memory systems, check out our guide on temporal memory intelligence and the full pricing comparison with Mem0.

Frequently Asked Questions

Are webhooks available on the Free plan? +
Yes. Webhooks are available on all plans, including Free. You can register up to 3 webhook endpoints on Free, 10 on Pro, 25 on Scale, and unlimited on Enterprise.
What happens if my endpoint is down? +
0Latency retries with exponential backoff up to 5 times over approximately 5.5 hours. If all retries fail, the event is stored and retrievable via the events API for 30 days. Your webhook is automatically paused after 100 consecutive failures, and you'll receive an email notification.
Can I filter webhooks by specific agents? +
Yes. The agent_id field in webhook registration is optional. If provided, you'll only receive events for that agent. If omitted, you'll receive events for all agents under your account. You can also register multiple webhooks with different agent filters.
How do I set the decay threshold for decay_threshold events? +
Decay thresholds are configured per-agent via the agent settings API. The default threshold is 0.40 (40% confidence). When any memory drops below this score, a memory.decay_threshold event fires. You can set different thresholds for different agents.
Is there a way to replay past events? +
Yes. The events API lets you query historical events by type, agent, and time range. You can replay them to a webhook endpoint using POST /webhooks/{id}/replay with a time range filter. This is useful for backfilling a new integration or debugging.

Start building with memory webhooks

Full webhook support on every plan. Set up real-time memory notifications in minutes.

Get your free API key →