Skip to main content

REST API: Webhooks

Real-time event notifications for memory changes in the Trix REST API.

Overview

Webhooks notify your application in real-time when events occur in Trix. Use them to sync memories with external systems, trigger workflows, or build reactive integrations.

Webhooks are delivered with at-least-once semantics. Your endpoint should be idempotent to handle potential duplicate deliveries.

Creating Webhooks

Create Webhook

create-webhook.sh
curl -X POST "https://api.trixdb.com/v1/webhooks" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-app.com/webhooks/trix",
    "events": ["memory.created", "memory.updated", "memory.deleted"],
    "space_id": "<space-uuid>",
    "description": "Sync memories to search index",
    "is_active": true
  }'

# Response - the plaintext secret is only shown once, at creation
{
  "id": "<webhook-uuid>",
  "url": "https://your-app.com/webhooks/trix",
  "events": ["memory.created", "memory.updated", "memory.deleted"],
  "space_id": "<space-uuid>",
  "secret": "<32-byte base64url-encoded secret - store securely>",
  "description": "Sync memories to search index",
  "is_active": true,
  "created_at": "2026-04-15T10:30:00Z",
  "last_triggered_at": null,
  "failure_count": 0
}

Configuration Options

FieldTypeDescription
urlstringHTTPS endpoint to receive events (required)
eventsstring[]Event types to subscribe to (required)
space_idstringLimit to a specific space (optional, UUID)
secretstringOptional. If omitted, the server generates one (32 random bytes, base64url) and returns it in the create response - this is the only time you'll see the plaintext. On subsequent GETs the secret field is masked.
descriptionstringHuman-readable description
is_activebooleanWhether webhook is active (default: true)

Event Types

Subscribe to specific events based on your integration needs:

Memory Events

EventDescription
memory.createdNew memory created
memory.updatedMemory content or metadata updated
memory.deletedMemory permanently deleted
memory.restoredSoft-deleted memory restored
memory.verifiedMemory verification status changed
memory.bulk_createdBatch of memories created via bulk endpoint
memory.bulk_updatedBatch of memories updated via bulk endpoint
memory.bulk_deletedBatch of memories deleted via bulk endpoint
memory.bulk_tags_updatedTag batch update

Relationship Events

EventDescription
relationship.createdNew relationship between memories
relationship.deletedRelationship removed

Cluster Events

EventDescription
cluster.createdNew cluster discovered
cluster.updatedCluster membership changed
cluster.mergedClusters were combined

Space Events

EventDescription
space.createdNew space created
space.updatedSpace settings updated
space.deletedSpace deleted

Wildcard Subscriptions

{
  "events": ["memory.*"]     // All memory events
  "events": ["*"]            // All events (use carefully)
}

Webhook Payload

Webhook payloads follow a consistent structure:

webhook-payload.json
{
  "id": "evt_abc123xyz",
  "type": "memory.created",
  "created_at": "2024-01-15T10:30:00Z",
  "space_id": "space_abc123",
  "data": {
    "memory": {
      "id": "mem_xyz789",
      "content": "User prefers dark mode",
      "tags": ["preferences", "ui"],
      "metadata": {},
      "created_at": "2024-01-15T10:30:00Z"
    }
  },
  "previous_data": null  // For updates, contains previous state
}

Update Events

Update events include both current and previous state:

update-payload.json
{
  "id": "evt_def456abc",
  "type": "memory.updated",
  "created_at": "2024-01-15T11:00:00Z",
  "data": {
    "memory": {
      "id": "mem_xyz789",
      "content": "User prefers dark mode and compact layout",
      "tags": ["preferences", "ui", "layout"]
    }
  },
  "previous_data": {
    "memory": {
      "id": "mem_xyz789",
      "content": "User prefers dark mode",
      "tags": ["preferences", "ui"]
    }
  },
  "changes": ["content", "tags"]
}

Signature Verification

Every webhook delivery carries an HMAC-SHA256 signature in the X-Webhook-Signature header. The signed string is {timestamp}.{payload} (the X-Webhook-Timestamp header concatenated with a literal dot and the raw request body), hashed with your webhook secret. The header value is t=<timestamp>,v1=<hex-signature>.

Webhook Headers
X-Webhook-ID: <webhook-uuid>
X-Webhook-Event: memory.created
X-Webhook-Timestamp: 1744728600
X-Webhook-Signature: t=1744728600,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
X-Webhook-Delivery-ID: <delivery-uuid>

Webhook secrets are 32 random bytes, base64url-encoded - there is no whsec_ or other prefix. Secrets are only shown once at creation and can be rotated via PATCH /v1/webhooks/:id.

Verification Examples

import crypto from 'crypto';

function verifyWebhook(
  payload: string,
  signatureHeader: string, // "t=<ts>,v1=<hex>"
  secret: string
): boolean {
  const match = /^t=(\d+),v1=([a-f0-9]+)$/.exec(signatureHeader);
  if (!match) return false;
  const [, timestamp, provided] = match;

  // Replay protection: 5-minute window
  const now = Math.floor(Date.now() / 1000);
  if (now - parseInt(timestamp, 10) > 300) return false;

  const signedPayload = `${timestamp}.${payload}`;
  const expected = crypto
    .createHmac('sha256', secret)
    .update(signedPayload)
    .digest('hex');

  return crypto.timingSafeEqual(Buffer.from(provided), Buffer.from(expected));
}

// Express example - note the raw body parser
app.post('/webhooks/trix', express.raw({type: 'application/json'}), (req, res) => {
  const header = req.headers['x-webhook-signature'] as string;
  if (!verifyWebhook(req.body.toString(), header, WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }
  const event = JSON.parse(req.body.toString());
  // ... handle event
  res.status(200).send('OK');
});

Managing Webhooks

List Webhooks

list-webhooks.sh
curl -X GET "https://api.trixdb.com/v1/webhooks" \
  -H "Authorization: Bearer YOUR_API_KEY"

# Response
{
  "webhooks": [
    {
      "id": "wh_abc123",
      "url": "https://your-app.com/webhooks/trix",
      "events": ["memory.created", "memory.updated"],
      "enabled": true,
      "failure_count": 0,
      "last_triggered_at": "2024-01-15T10:30:00Z"
    }
  ],
  "total": 1
}

Update Webhook

update-webhook.sh
curl -X PATCH "https://api.trixdb.com/v1/webhooks/wh_abc123" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "events": ["memory.*"],
    "enabled": true
  }'

Delete Webhook

delete-webhook.sh
curl -X DELETE "https://api.trixdb.com/v1/webhooks/wh_abc123" \
  -H "Authorization: Bearer YOUR_API_KEY"

Test Webhook

Send a test event to verify your endpoint:

test-webhook.sh
curl -X POST "https://api.trixdb.com/v1/webhooks/wh_abc123/test" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "event_type": "memory.created"
  }'

# Response
{
  "success": true,
  "response_status": 200,
  "response_time_ms": 145,
  "response_body": "OK"
}

Delivery & Retries

Trix implements automatic retries with exponential backoff for failed deliveries.

Retry Schedule

AttemptDelayTotal Time
1Immediate0
21 minute1 min
35 minutes6 min
430 minutes36 min
52 hours~2.5 hours
68 hours~10.5 hours

Success Criteria

A delivery is considered successful when:

  • Your endpoint responds with a 2xx status code
  • Response is received within 30 seconds

Failure Handling

After 6 failed attempts, the webhook is automatically disabled. Re-enable it after fixing the issue:

reenable-webhook.sh
curl -X PATCH "https://api.trixdb.com/v1/webhooks/wh_abc123" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"enabled": true}'

Delivery Logs

View recent delivery attempts:

delivery-logs.sh
curl -X GET "https://api.trixdb.com/v1/webhooks/wh_abc123/deliveries?limit=10" \
  -H "Authorization: Bearer YOUR_API_KEY"

# Response
{
  "deliveries": [
    {
      "id": "del_xyz789",
      "event_id": "evt_abc123",
      "event_type": "memory.created",
      "status": "success",
      "response_status": 200,
      "response_time_ms": 142,
      "attempt": 1,
      "created_at": "2024-01-15T10:30:00Z"
    },
    {
      "id": "del_xyz788",
      "event_id": "evt_abc122",
      "event_type": "memory.updated",
      "status": "failed",
      "response_status": 500,
      "error": "Internal Server Error",
      "attempt": 1,
      "created_at": "2024-01-15T10:29:00Z",
      "next_retry_at": "2024-01-15T10:30:00Z"
    }
  ]
}

Best Practices

  • Respond quickly - Return 200 immediately, process asynchronously
  • Be idempotent - Use X-Trix-Delivery-ID to deduplicate
  • Verify signatures - Always validate X-Trix-Signature
  • Check timestamps - Reject events older than 5 minutes
  • Use HTTPS - Webhook URLs must use HTTPS in production
  • Monitor failures - Set up alerts for webhook delivery failures

For high-volume applications, consider using a message queue (Redis, RabbitMQ, SQS) to buffer webhook events and process them asynchronously.

Endpoints Reference

MethodEndpointDescription
POST/webhooksCreate webhook
GET/webhooksList webhooks
GET/webhooks/:idGet webhook details
PATCH/webhooks/:idUpdate webhook
DELETE/webhooks/:idDelete webhook
POST/webhooks/:id/testSend test event
GET/webhooks/:id/deliveriesView delivery logs