Mindset v3 / Deploy in your app / Webhooks
View as MarkdownWebhooks API - User Guide
Receive notifications when changes are made to your Mindset AI resources
What are Webhooks?
Webhooks allow you to be notified when changes are made to parts of the Mindset AI system—without needing to repeatedly check by calling an API.
You can use webhooks to:
- Be notified of changes made by users to agent configuration
- Monitor processing status of files you send to the ContextFiles API for ingestion
- Track when contexts are created, updated, or deleted
- Receive notifications when agents are modified
When these events occur, you'll automatically receive notifications at your specified endpoint.
Real-World Example
When a tenant admin creates a new AI agent, you receive a notification allowing you to:
Update your database automatically
Send notifications to other administrators
Trigger custom workflows
Update analytics dashboards and log audit trails
Why Use Webhooks?
Automatic notifications → Minimal load, immediate response
Poll APIs every few seconds → High server load, delayed response
- Constant polling increases server load
- Delayed detection of changes
- Higher API usage and costs
- Increased latency in response
Supported Events
Webhooks fire for 3 resource types:
Agents
Events:
agent.createdagent.updatedagent.deleted
Description: AI agents configured by tenants
Contexts
Events:
context.createdcontext.updatedcontext.deleted
Description: Knowledge bases and document collections
Context Files
Events:
contextfile.createdcontextfile.updatedcontextfile.deleted
Description: Files uploaded to contexts
Quick Start Guide
Step 1: Create Your Webhook Endpoint
Create an HTTPS endpoint that accepts POST requests:
// Example: Express.js
app.post('/webhooks/mindset', async (req, res) => {
// 1. Verify signature (security - see below)
const isValid = verifyWebhookSignature(req);
if (!isValid) {
return res.status(401).json({ error: 'Invalid signature' });
}
// 2. Process webhook event
const event = req.body;
console.log(`Received ${event.event} for tenant ${event.data.externalTenantId}`);
// 3. Respond quickly (< 5 seconds)
res.status(200).json({ received: true });
// 4. Process heavy work asynchronously
processWebhookAsync(event);
});Step 2: Register Your Webhook
API Endpoint:
POST https://{environment}.api.mindset.ai/api/v1/appuid/{appUid}/webhooksRequest:
curl -X POST \
https://your-environment.api.mindset.ai/api/v1/appuid/your-app-uid/webhooks \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-platform.com/webhooks/mindset",
"entities": ["agent", "context", "contextfile"],
"active": true
}'Response:
{
"uid": "webhook-abc123",
"url": "https://your-platform.com/webhooks/mindset",
"secret": "whsec_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6",
"entities": ["agent", "context", "contextfile"],
"active": true,
"createdAt": "2025-01-20T14:30:00Z"
}Step 3: Test Your Integration
Make a Change
Make a change in the AMS SDK (create an agent, upload a file, etc.)
Verify Receipt
Verify your endpoint receives the webhook
Check Logs
Check the browser console or server logs
Webhook Payload Structure
HTTP Headers
Content-Type: application/json
X-Webhook-Signature: sha256=<HMAC-SHA256 signature>
X-Webhook-Event-Id: <unique event ID>
X-Webhook-Timestamp: <ISO8601 timestamp>Payload Example
{
"eventId": "evt_A1b2C3d4E5f6",
"event": "agent.created",
"timestamp": "2025-01-20T14:35:22.123Z",
"appUid": "your-app-uid",
"version": 1,
"data": {
"uid": "agent-abc123",
"appUid": "app-123",
"agentName": "Customer Support Agent",
"externalTenantId": "tenant-xyz789",
"live": true,
"createdAt": "2025-01-20T14:35:22Z"
// ... complete agent object
}
}Key Fields
| Field | Type | Description |
|---|---|---|
| eventId | string | Unique event identifier (use for idempotency) |
| event | string | Event name (e.g., agent.created) |
| timestamp | string | When event occurred (ISO8601) |
| data | object | Complete resource object |
| data.externalTenantId | string | Identifies which tenant made the change |
Security: Verify Signatures
import crypto from 'crypto';
function verifyWebhookSignature(payload, signature, secret) {
// Remove 'sha256=' prefix
const providedSig = signature.startsWith('sha256=')
? signature.slice(7)
: signature;
// Compute HMAC-SHA256
const computedSig = crypto
.createHmac('sha256', secret)
.update(payload, 'utf8')
.digest('hex');
// Constant-time comparison (prevents timing attacks)
return crypto.timingSafeEqual(
Buffer.from(providedSig, 'hex'),
Buffer.from(computedSig, 'hex')
);
}
// Express.js middleware
app.post('/webhooks/mindset', (req, res) => {
const signature = req.headers['x-webhook-signature'];
const payload = JSON.stringify(req.body);
const secret = process.env.MINDSET_WEBHOOK_SECRET;
if (!verifyWebhookSignature(payload, signature, secret)) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Process webhook...
res.status(200).json({ received: true });
});import hmac
import hashlib
def verify_webhook_signature(payload: str, signature: str, secret: str) -> bool:
# Remove 'sha256=' prefix
if signature.startswith('sha256='):
signature = signature[7:]
# Compute HMAC-SHA256
computed = hmac.new(
secret.encode('utf-8'),
payload.encode('utf-8'),
hashlib.sha256
).hexdigest()
# Constant-time comparison
return hmac.compare_digest(computed, signature)
# Flask example
@app.route('/webhooks/mindset', methods=['POST'])
def webhook_handler():
signature = request.headers.get('X-Webhook-Signature')
payload = request.get_data(as_text=True)
secret = os.environ['MINDSET_WEBHOOK_SECRET']
if not verify_webhook_signature(payload, signature, secret):
return jsonify({'error': 'Invalid signature'}), 401
event = request.json
handle_webhook_event(event)
return jsonify({'received': True}), 200Handle Webhook Events
Basic Event Handling
async function handleWebhookEvent(event) {
const { event: eventType, data } = event;
console.log(`Processing ${eventType} for tenant ${data.externalTenantId}`);
switch (eventType) {
case 'agent.created':
await db.agents.create({
id: data.uid,
name: data.agentName,
tenantId: data.externalTenantId
});
break;
case 'agent.updated':
await db.agents.update(data.uid, {
name: data.agentName
});
break;
case 'agent.deleted':
await db.agents.delete(data.uid);
break;
// Handle context events...
case 'context.created':
case 'context.updated':
case 'context.deleted':
// Your logic here
break;
// Handle file events...
case 'contextfile.created':
case 'contextfile.updated':
case 'contextfile.deleted':
// Your logic here
break;
}
}Prevent Duplicate Processing (Idempotency)
Webhooks may be delivered multiple times. Use eventId to process each event only once:
async function handleWebhookEvent(event) {
// Check if already processed
const exists = await db.processedEvents.findOne({ eventId: event.eventId });
if (exists) {
console.log(`Event ${event.eventId} already processed`);
return;
}
// Process the event
await processEvent(event);
// Mark as processed
await db.processedEvents.create({
eventId: event.eventId,
processedAt: new Date(),
event: event.event
});
}Respond Quickly (Async Processing)
Respond within 5 seconds. Move heavy work to background:
app.post('/webhooks/mindset', async (req, res) => {
// Verify signature...
// ✅ Respond immediately
res.status(200).json({ received: true });
// ✅ Process asynchronously
processWebhookAsync(req.body).catch(error => {
console.error('Webhook processing failed:', error);
});
});Manage Webhooks
List All Webhooks
GET /api/v1/appuid/{appUid}/webhooksUpdate Webhook
PATCH /api/v1/appuid/{appUid}/webhooks/{webhookUid}
{
"url": "https://your-platform.com/webhooks/mindset-v2",
"entities": ["agent", "context", "contextfile"],
"active": true
}Pause Webhook
PATCH /api/v1/appuid/{appUid}/webhooks/{webhookUid}
{
"active": false
}Delete Webhook
DELETE /api/v1/appuid/{appUid}/webhooks/{webhookUid}Best Practices
Security ✅
- Always verify signatures
- Use HTTPS endpoints
- Store secrets securely
- Rotate secrets periodically
Reliability ✅
- Respond within 5 seconds
- Implement idempotency using
eventId - Handle failures gracefully
- Process heavy work asynchronously
Monitoring ✅
- Log successful and failed deliveries
- Monitor
lastTriggeredAttimestamps - Alert on extended periods without webhooks
Troubleshooting
Webhooks Not Received
Check Webhook Status
Webhook is registered and
active: trueVerify Endpoint URL
Endpoint URL is correct and accessible
Check Firewall
Firewall allows HTTPS traffic from Mindset servers
Signature Verification Fails
Check:
Duplicate Deliveries
Solution: Implement idempotency using eventId (see example above)
Complete Working Example
import express from 'express';
import crypto from 'crypto';
const app = express();
const WEBHOOK_SECRET = process.env.MINDSET_WEBHOOK_SECRET;
app.post('/webhooks/mindset',
express.raw({ type: 'application/json' }),
async (req, res) => {
// 1. Verify signature
const signature = req.headers['x-webhook-signature'];
const payload = req.body.toString('utf8');
if (!verifyWebhookSignature(payload, signature, WEBHOOK_SECRET)) {
return res.status(401).json({ error: 'Invalid signature' });
}
// 2. Parse and respond quickly
const event = JSON.parse(payload);
res.status(200).json({ received: true });
// 3. Process async
processWebhookAsync(event);
}
);
function verifyWebhookSignature(payload, signature, secret) {
const providedSig = signature.replace('sha256=', '');
const computedSig = crypto
.createHmac('sha256', secret)
.update(payload, 'utf8')
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(providedSig, 'hex'),
Buffer.from(computedSig, 'hex')
);
}
async function processWebhookAsync(event) {
// Check idempotency
if (await isEventProcessed(event.eventId)) return;
// Handle event
switch (event.event) {
case 'agent.created':
await db.agents.create(event.data);
break;
// ... other events
}
// Mark processed
await markEventProcessed(event.eventId);
}
app.listen(3000, () => console.log('Webhook server running'));