Skip to content

Webhooks

Real-time event notifications via HTTP callbacks

Webhooks

Webhooks deliver real-time event notifications to your HTTP endpoints. When events occur in Engram (memory stored, compressed, decayed, etc.), Engram sends a POST request to your configured URL with event details.

All webhook payloads are signed with HMAC-SHA256 so you can verify authenticity.

Create Webhook

Register a new webhook endpoint.

POST /v1/webhooks

Request Body

ParameterTypeRequiredDescription
urlstringYesHTTPS endpoint URL to receive events
eventsarray[string]YesEvent types to subscribe to
enabledbooleanNoWhether webhook is active (default: true)
secretstringNoSigning secret for HMAC verification. Auto-generated if omitted.
headersobjectNoCustom headers to include in webhook requests
timeout_secondsintegerNoRequest timeout (default: 10, max: 30)
max_retriesintegerNoMaximum retry attempts on failure (default: 3, max: 10)
retry_backoff_secondsintegerNoBase backoff interval between retries (default: 60)

Event Types

EventDescription
MEMORY_STOREDNew memory created
MEMORY_RECALLEDMemory retrieved via search or recall
MEMORY_COMPRESSEDMemory vectors compressed
MEMORY_DECAYEDMemory importance decayed below threshold
MEMORY_FORGOTTENMemory permanently deleted
COLLECTION_CREATEDNew collection created
USAGE_THRESHOLDUsage quota threshold reached (80%, 90%, 100%)

Code Examples

cURL

curl -X POST https://api.engrammemory.ai/v1/webhooks \
  -H "Authorization: Bearer pr_live_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/webhooks/engram",
    "events": ["MEMORY_STORED", "MEMORY_FORGOTTEN", "USAGE_THRESHOLD"],
    "headers": {
      "X-Custom-Header": "my-value"
    },
    "timeout_seconds": 15,
    "max_retries": 5,
    "retry_backoff_seconds": 120
  }'

Python

from engrammemory import Engram

client = Engram(api_key="pr_live_xxxxx")

webhook = client.webhooks.create(
    url="https://example.com/webhooks/engram",
    events=["MEMORY_STORED", "MEMORY_FORGOTTEN", "USAGE_THRESHOLD"],
    headers={"X-Custom-Header": "my-value"},
    timeout_seconds=15,
    max_retries=5,
    retry_backoff_seconds=120,
)
print(f"Webhook ID: {webhook.id}")
print(f"Secret: {webhook.secret}")  # Store this for verification

JavaScript

import { Engram } from "engrammemory-ai";

const client = new Engram({ apiKey: "pr_live_xxxxx" });

const webhook = await client.webhooks.create({
  url: "https://example.com/webhooks/engram",
  events: ["MEMORY_STORED", "MEMORY_FORGOTTEN", "USAGE_THRESHOLD"],
  headers: { "X-Custom-Header": "my-value" },
  timeoutSeconds: 15,
  maxRetries: 5,
  retryBackoffSeconds: 120,
});
console.log(`Webhook ID: ${webhook.id}`);
console.log(`Secret: ${webhook.secret}`); // Store this for verification

Response

{
  "id": "wh_abc123",
  "url": "https://example.com/webhooks/engram",
  "events": ["MEMORY_STORED", "MEMORY_FORGOTTEN", "USAGE_THRESHOLD"],
  "enabled": true,
  "secret": "whsec_7f3a9b2c4d5e6f...",
  "headers": {
    "X-Custom-Header": "my-value"
  },
  "timeout_seconds": 15,
  "max_retries": 5,
  "retry_backoff_seconds": 120,
  "created_at": "2026-04-10T12:00:00Z"
}

List Webhooks

List all webhooks for your account.

GET /v1/webhooks

Code Examples

cURL

curl https://api.engrammemory.ai/v1/webhooks \
  -H "Authorization: Bearer pr_live_xxxxx"

Python

webhooks = client.webhooks.list()
for wh in webhooks.items:
    print(f"{wh.id}: {wh.url} ({len(wh.events)} events)")

JavaScript

const webhooks = await client.webhooks.list();
webhooks.items.forEach((wh) => {
  console.log(`${wh.id}: ${wh.url} (${wh.events.length} events)`);
});

Response

{
  "items": [
    {
      "id": "wh_abc123",
      "url": "https://example.com/webhooks/engram",
      "events": ["MEMORY_STORED", "MEMORY_FORGOTTEN"],
      "enabled": true,
      "created_at": "2026-04-10T12:00:00Z",
      "last_delivery_at": "2026-04-10T14:30:00Z",
      "last_delivery_status": "SUCCESS"
    }
  ],
  "total": 2
}

Update Webhook

Update an existing webhook configuration.

PATCH /v1/webhooks/{webhook_id}

Path Parameters

ParameterTypeRequiredDescription
webhook_idstringYesWebhook ID

Request Body

All fields are optional. Only include fields you want to update.

ParameterTypeDescription
urlstringEndpoint URL
eventsarray[string]Event types
enabledbooleanActive state
headersobjectCustom headers
timeout_secondsintegerRequest timeout
max_retriesintegerRetry attempts
retry_backoff_secondsintegerBackoff interval

Code Examples

cURL

curl -X PATCH https://api.engrammemory.ai/v1/webhooks/wh_abc123 \
  -H "Authorization: Bearer pr_live_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "events": ["MEMORY_STORED", "MEMORY_COMPRESSED"],
    "enabled": false
  }'

Python

webhook = client.webhooks.update(
    "wh_abc123",
    events=["MEMORY_STORED", "MEMORY_COMPRESSED"],
    enabled=False,
)

JavaScript

const webhook = await client.webhooks.update("wh_abc123", {
  events: ["MEMORY_STORED", "MEMORY_COMPRESSED"],
  enabled: false,
});

Response

Returns the updated webhook object.


Delete Webhook

Permanently delete a webhook. Pending deliveries will be cancelled.

DELETE /v1/webhooks/{webhook_id}

Path Parameters

ParameterTypeRequiredDescription
webhook_idstringYesWebhook ID

Code Examples

cURL

curl -X DELETE https://api.engrammemory.ai/v1/webhooks/wh_abc123 \
  -H "Authorization: Bearer pr_live_xxxxx"

Python

client.webhooks.delete("wh_abc123")

JavaScript

await client.webhooks.delete("wh_abc123");

Response

204 No Content


Test Webhook

Send a test event to verify your endpoint is working correctly.

POST /v1/webhooks/{webhook_id}/test

Path Parameters

ParameterTypeRequiredDescription
webhook_idstringYesWebhook ID

Code Examples

cURL

curl -X POST https://api.engrammemory.ai/v1/webhooks/wh_abc123/test \
  -H "Authorization: Bearer pr_live_xxxxx"

Python

result = client.webhooks.test("wh_abc123")
print(f"Status: {result.status_code}")
print(f"Response time: {result.response_time_ms} ms")

JavaScript

const result = await client.webhooks.test("wh_abc123");
console.log(`Status: ${result.statusCode}`);
console.log(`Response time: ${result.responseTimeMs} ms`);

Response

{
  "delivery_id": "del_test_xyz",
  "status_code": 200,
  "response_time_ms": 145,
  "response_body": "OK",
  "success": true,
  "event_type": "TEST"
}

Webhook Analytics

Get performance statistics for a specific webhook.

GET /v1/webhooks/{webhook_id}/analytics

Path Parameters

ParameterTypeRequiredDescription
webhook_idstringYesWebhook ID

Code Examples

cURL

curl https://api.engrammemory.ai/v1/webhooks/wh_abc123/analytics \
  -H "Authorization: Bearer pr_live_xxxxx"

Python

analytics = client.webhooks.analytics("wh_abc123")
print(f"Success rate: {analytics.success_rate_pct}%")
print(f"Avg response time: {analytics.avg_response_time_ms} ms")

JavaScript

const analytics = await client.webhooks.analytics("wh_abc123");
console.log(`Success rate: ${analytics.successRatePct}%`);
console.log(`Avg response time: ${analytics.avgResponseTimeMs} ms`);

Response

{
  "webhook_id": "wh_abc123",
  "total_deliveries": 12450,
  "successful_deliveries": 12380,
  "failed_deliveries": 70,
  "success_rate_pct": 99.44,
  "avg_response_time_ms": 132,
  "p95_response_time_ms": 340,
  "p99_response_time_ms": 780,
  "events_breakdown": {
    "MEMORY_STORED": 8200,
    "MEMORY_FORGOTTEN": 3100,
    "USAGE_THRESHOLD": 1150
  },
  "period": "last_30_days"
}

Delivery History

Get the delivery history for a specific webhook.

GET /v1/webhooks/{webhook_id}/deliveries

Path Parameters

ParameterTypeRequiredDescription
webhook_idstringYesWebhook ID

Query Parameters

ParameterTypeRequiredDescription
pageintegerNoPage number (default: 1)
page_sizeintegerNoResults per page (default: 20, max: 100)
statusstringNoFilter by SUCCESS or FAILED
event_typestringNoFilter by event type

Code Examples

cURL

curl "https://api.engrammemory.ai/v1/webhooks/wh_abc123/deliveries?page=1&status=FAILED" \
  -H "Authorization: Bearer pr_live_xxxxx"

Python

deliveries = client.webhooks.deliveries("wh_abc123", status="FAILED", page=1)
for d in deliveries.items:
    print(f"{d.id}: {d.event_type} — {d.status_code} ({d.error})")

JavaScript

const deliveries = await client.webhooks.deliveries("wh_abc123", {
  status: "FAILED",
  page: 1,
});
deliveries.items.forEach((d) => {
  console.log(`${d.id}: ${d.eventType} — ${d.statusCode} (${d.error})`);
});

Response

{
  "items": [
    {
      "id": "del_abc123",
      "event_type": "MEMORY_STORED",
      "status": "SUCCESS",
      "status_code": 200,
      "response_time_ms": 120,
      "attempt": 1,
      "max_attempts": 5,
      "request_body": "{\"event\":\"MEMORY_STORED\",\"data\":{...}}",
      "response_body": "OK",
      "error": null,
      "delivered_at": "2026-04-10T14:30:00Z"
    },
    {
      "id": "del_def456",
      "event_type": "MEMORY_FORGOTTEN",
      "status": "FAILED",
      "status_code": 500,
      "response_time_ms": 2340,
      "attempt": 3,
      "max_attempts": 5,
      "request_body": "{\"event\":\"MEMORY_FORGOTTEN\",\"data\":{...}}",
      "response_body": "Internal Server Error",
      "error": "Server returned 500",
      "delivered_at": "2026-04-10T14:25:00Z",
      "next_retry_at": "2026-04-10T14:33:00Z"
    }
  ],
  "total": 70,
  "page": 1,
  "page_size": 20,
  "total_pages": 4
}

HMAC Signature Verification

Every webhook delivery includes an X-Engram-Signature header containing an HMAC-SHA256 signature. Use your webhook secret to verify the payload was sent by Engram.

Header Format

X-Engram-Signature: sha256=<hex-encoded-hmac>
X-Engram-Delivery-Id: del_abc123
X-Engram-Event: MEMORY_STORED
X-Engram-Timestamp: 1712750400

Verification Examples

Python

import hmac
import hashlib

def verify_webhook(payload_body: bytes, signature_header: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode("utf-8"),
        payload_body,
        hashlib.sha256,
    ).hexdigest()
    received = signature_header.replace("sha256=", "")
    return hmac.compare_digest(expected, received)

# In your webhook handler:
# verify_webhook(request.body, request.headers["X-Engram-Signature"], "whsec_...")

JavaScript

import crypto from "crypto";

function verifyWebhook(payloadBody, signatureHeader, secret) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(payloadBody, "utf-8")
    .digest("hex");
  const received = signatureHeader.replace("sha256=", "");
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(received)
  );
}

// In your webhook handler:
// verifyWebhook(req.body, req.headers["x-engram-signature"], "whsec_...");

Retry Logic

Failed deliveries are retried with exponential backoff:

AttemptDelay
1Immediate
2retry_backoff_seconds (default: 60s)
3retry_backoff_seconds * 2 (120s)
4retry_backoff_seconds * 4 (240s)
5retry_backoff_seconds * 8 (480s)

A delivery is considered failed when:

  • Response status code is 4xx or 5xx
  • Connection timeout exceeded
  • DNS resolution failure
  • TLS handshake failure

After all retries are exhausted, the delivery is marked as FAILED permanently. Check the delivery history endpoint to monitor failures.


Webhook Payload Format

All webhook payloads follow this structure:

{
  "id": "evt_abc123",
  "event": "MEMORY_STORED",
  "timestamp": "2026-04-10T14:30:00Z",
  "data": {
    "memory_id": "mem_xyz789",
    "content": "The stored memory content...",
    "user_id": "user123",
    "metadata": {
      "category": "documentation",
      "importance": 0.8
    }
  }
}

Error Codes

CodeHTTP StatusDescription
INVALID_URL400URL is not valid HTTPS
INVALID_EVENTS400Events array is empty or contains invalid event types
INVALID_TIMEOUT400Timeout out of range (1-30 seconds)
WEBHOOK_NOT_FOUND404Webhook ID does not exist
WEBHOOK_LIMIT_EXCEEDED409Maximum webhooks per account reached (default: 20)
WEBHOOK_URL_UNREACHABLE422URL failed connectivity check during creation
RATE_LIMIT_EXCEEDED429Too many webhook management requests