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
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
| Field | Type | Description |
|---|---|---|
url | string | HTTPS endpoint to receive events (required) |
events | string[] | Event types to subscribe to (required) |
space_id | string | Limit to a specific space (optional, UUID) |
secret | string | Optional. 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. |
description | string | Human-readable description |
is_active | boolean | Whether webhook is active (default: true) |
Event Types
Subscribe to specific events based on your integration needs:
Memory Events
| Event | Description |
|---|---|
memory.created | New memory created |
memory.updated | Memory content or metadata updated |
memory.deleted | Memory permanently deleted |
memory.restored | Soft-deleted memory restored |
memory.verified | Memory verification status changed |
memory.bulk_created | Batch of memories created via bulk endpoint |
memory.bulk_updated | Batch of memories updated via bulk endpoint |
memory.bulk_deleted | Batch of memories deleted via bulk endpoint |
memory.bulk_tags_updated | Tag batch update |
Relationship Events
| Event | Description |
|---|---|
relationship.created | New relationship between memories |
relationship.deleted | Relationship removed |
Cluster Events
| Event | Description |
|---|---|
cluster.created | New cluster discovered |
cluster.updated | Cluster membership changed |
cluster.merged | Clusters were combined |
Space Events
| Event | Description |
|---|---|
space.created | New space created |
space.updated | Space settings updated |
space.deleted | Space deleted |
Wildcard Subscriptions
{
"events": ["memory.*"] // All memory events
"events": ["*"] // All events (use carefully)
}Webhook Payload
Webhook payloads follow a consistent structure:
{
"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:
{
"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>.
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
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
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
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:
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
| Attempt | Delay | Total Time |
|---|---|---|
| 1 | Immediate | 0 |
| 2 | 1 minute | 1 min |
| 3 | 5 minutes | 6 min |
| 4 | 30 minutes | 36 min |
| 5 | 2 hours | ~2.5 hours |
| 6 | 8 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:
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:
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-IDto 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
| Method | Endpoint | Description |
|---|---|---|
POST | /webhooks | Create webhook |
GET | /webhooks | List webhooks |
GET | /webhooks/:id | Get webhook details |
PATCH | /webhooks/:id | Update webhook |
DELETE | /webhooks/:id | Delete webhook |
POST | /webhooks/:id/test | Send test event |
GET | /webhooks/:id/deliveries | View delivery logs |