DenchClaw Event System: Real-Time Updates Across Your Workspace
How DenchClaw's event system broadcasts real-time updates across all open apps and views when CRM data changes.
DenchClaw Event System: Real-Time Updates Across Your Workspace
DenchClaw's event system is the real-time communication layer that connects your CRM, AI agent, and custom apps. When a deal status changes, an entry is created, or an AI task completes, an event is broadcast instantly to all listeners — other apps, widgets, view components, and external webhooks — keeping your entire workspace in sync without polling.
Architecture Overview#
The event system is built on a local publish-subscribe model:
CRM Update → Event Bus → [App Listeners, View Listeners, Webhook Listeners, ...]
AI Action → Event Bus → [App Listeners, Notification System, ...]
App Action → Event Bus → [Other Apps, AI Agent, ...]
Every component in DenchClaw — views, apps, widgets, the AI agent, cron jobs — both emits and subscribes to events. The event bus is the nervous system of the workspace.
System Events Reference#
These events are emitted by DenchClaw automatically:
CRM Events#
// Entry lifecycle events
"entry:created" // New entry added to any object
"entry:updated" // Entry fields changed
"entry:deleted" // Entry removed
"entry:restored" // Deleted entry restored
// Payload for entry events:
{
objectName: "deals",
entryId: "abc-123",
fields: { /* all field values */ },
changedFields: { /* for updates: only changed fields */ }
}// Object/schema events
"object:created" // New CRM object created
"object:updated" // Object schema modified
"object:deleted" // Object deleted
// View events
"view:changed" // User switched to a different view
"view:filtered" // Active filter changed
"selection:changed" // User selected entries in the current viewAI Agent Events#
"agent:message" // New chat message (user or AI)
"agent:thinking" // AI is processing
"agent:done" // AI response completed
"agent:subagent:started" // Subagent spawned
"agent:subagent:completed" // Subagent finished
"agent:tool_call" // AI called a tool
"agent:tool_result" // Tool returned a resultApp Events#
"app:loaded" // A Dench App opened
"app:closed" // A Dench App closed
"app:error" // App encountered an errorSystem Events#
"cron:started" // Scheduled task began
"cron:completed" // Scheduled task finished
"cron:failed" // Scheduled task errored
"webhook:received" // Inbound webhook arrived
"workspace:synced" // Workspace data synced (cloud sync)Subscribing to Events in Apps#
// Subscribe to a specific event
dench.events.on("entry:created", (event) => {
if (event.objectName === "deals") {
console.log("New deal:", event.fields["Deal Name"]);
}
});
// Subscribe with filter function
dench.events.on("entry:updated", (event) => {
if (event.objectName === "deals" &&
event.changedFields?.["Status"] === "Closed Won") {
celebrate(); // 🎉
}
});
// Subscribe to all events (useful for debugging)
dench.events.on("*", (event) => {
console.log(event.type, event);
});
// One-time subscription
dench.events.once("agent:done", (event) => {
console.log("AI task finished:", event.result);
});
// Unsubscribe
const handler = (event) => { /* ... */ };
dench.events.on("entry:created", handler);
// Later:
dench.events.off("entry:created", handler);Emitting Custom Events#
Apps emit custom events to communicate with each other:
// App-specific event (namespaced to avoid collisions)
await dench.events.emit("pipeline-dashboard:filter-changed", {
filter: "Q1 Deals",
dealCount: 42,
totalValue: 1250000
});
// Another app listens for it
dench.events.on("pipeline-dashboard:filter-changed", (event) => {
updateSummaryCard(event.dealCount, event.totalValue);
});Event-Driven Live Views#
The most powerful application: live-updating data views that react instantly to CRM changes.
// Live pipeline chart that updates when deals change
let chartData = null;
async function loadChart() {
const data = await dench.db.query(`
SELECT "Stage", COUNT(*) as count, SUM(CAST("Deal Value" AS FLOAT)) as total
FROM v_deals WHERE "Status" = 'Active'
GROUP BY "Stage"
`);
chartData = data;
renderChart(data);
}
// Initial load
await loadChart();
// React to CRM changes — no polling needed
dench.events.on("entry:created", async (e) => {
if (e.objectName === "deals") await loadChart();
});
dench.events.on("entry:updated", async (e) => {
if (e.objectName === "deals") await loadChart();
});
dench.events.on("entry:deleted", async (e) => {
if (e.objectName === "deals") await loadChart();
});Your chart now updates the instant a deal changes — without any polling loop or refresh button.
AI Agent Event Integration#
The AI agent integrates deeply with the event system:
The agent listens to app events: When your custom app emits an event, the agent can react:
// App emits a request for AI assistance
await dench.events.emit("app:help-requested", {
context: "User is viewing deal pipeline",
question: "Which deals are at risk this quarter?"
});
// Agent receives this and generates a response
// The response arrives via agent:message event
dench.events.on("agent:message", (event) => {
if (event.replyTo === "app:help-requested") {
displayAIInsight(event.content);
}
});The agent emits events from tool calls:
When the AI creates or updates a CRM entry, an entry:created or entry:updated event fires — keeping your open views and apps in sync.
Event Delivery Guarantees#
Events in DenchClaw are:
- In-order: Events are delivered in the order they occurred
- Synchronous delivery: All listeners are called before the emit resolves
- No persistence: Events are not stored (use the history API for recent events)
- Local scope: Events are scoped to the running workspace instance
For cross-session or cross-machine event delivery, use webhooks instead.
Performance Considerations#
The event system adds negligible overhead for typical usage. However:
- Avoid heavy computations in event handlers — they block the event loop
- For expensive operations (database queries, AI calls), debounce rapid events:
let debounceTimer;
dench.events.on("entry:updated", (e) => {
if (e.objectName !== "deals") return;
clearTimeout(debounceTimer);
debounceTimer = setTimeout(async () => {
await refreshDashboard();
}, 500); // Wait 500ms after last update before refreshing
});This prevents redundant refreshes when bulk operations update many records rapidly.
See also: DenchClaw Inter-App Messaging for app-to-app communication patterns, and DenchClaw Webhooks for external event delivery.
Frequently Asked Questions#
Can I subscribe to events from the AI chat?#
Yes. Subscribe to agent:message to receive all AI responses in your app. This enables custom UIs that overlay AI output on your data — for example, an AI assistant panel that responds to context from your currently active CRM view.
Do events fire during bulk imports?#
Yes. Each entry created during a bulk import fires an entry:created event. For large imports (1000+ entries), this can cause many rapid events. Use debouncing in your event handlers for bulk-import scenarios.
Can events trigger external webhooks?#
Yes — configure outbound webhooks to trigger on system events. See the DenchClaw Webhooks guide for event-to-webhook configuration.
Is there a way to replay historical events?#
Use dench.events.getHistory({ limit: 100, type: "entry:created" }) to retrieve recent events. History is limited to the last 100 events per type in the current session.
Can events be emitted from cron jobs?#
Yes. Tasks running in cron contexts have full event system access. A cron job can emit a completion event that triggers an update in any currently open app: dench.events.emit("cron:enrichment-complete", { newContactsAdded: 12 }).
Ready to try DenchClaw? Install in one command: npx denchclaw. Full setup guide →
