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 Database Update your database automatically
Send Notifications Send notifications to other administrators
Trigger Workflows Trigger custom workflows
Update Analytics Update analytics dashboards and log audit trails
Why Use Webhooks?
With Webhooks
Without Webhooks
Automatic notifications → Minimal load, immediate response Notified within 5 seconds of changes
No need to constantly poll APIs
React immediately to tenant actions
Every change recorded with timestamps
Supported Events
Webhooks fire for 3 resource types:
Events:
agent.created
agent.updated
agent.deleted
Description: AI agents configured by tenants
Events:
context.created
context.updated
context.deleted
Description: Knowledge bases and document collections
Events:
contextfile.created
contextfile.updated
contextfile.deleted
Description: Files uploaded to contexts
Webhooks fire for all tenants in your app. Use the externalTenantId field in each payload to identify which tenant made the change.
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 );
});
Security: Webhook endpoints MUST use HTTPS in production.
Step 2: Register Your Webhook
API Endpoint:
POST https://{environment}.api.mindset.ai/api/v1/appuid/{appUid}/webhooks
Request:
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"
}
Important: Save the secret! You’ll need it to verify webhook signatures. Store it securely (environment variable or secrets manager).
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
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
Critical: Always verify webhook signatures to prevent unauthorized requests.
Node.js Implementation
Python Implementation
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 });
});
Handle 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 );
});
});
Always respond to webhook requests within 5 seconds to prevent timeouts.
Manage Webhooks
List All Webhooks
GET /api/v1/appuid/{appUid}/webhooks
Update 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
Always verify signatures
Use HTTPS endpoints
Store secrets securely
Rotate secrets periodically
Respond within 5 seconds
Implement idempotency using eventId
Handle failures gracefully
Process heavy work asynchronously
Log successful and failed deliveries
Monitor lastTriggeredAt timestamps
Alert on extended periods without webhooks
Troubleshooting
Webhooks Not Received
Check Webhook Status
Webhook is registered and active: true
Verify Endpoint URL
Endpoint URL is correct and accessible
Check Firewall
Firewall allows HTTPS traffic from Mindset servers
Signature Verification Fails
Check:
Using correct secret from registration response
Using raw request body (not parsed JSON)
UTF-8 encoding is correct
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' ));