Event Pipeline
How to integrate your Butler with The Butler's event pipeline. A central event bus for cross-Butler updates enables automation and consistent state without tight coupling.
Overview
The Butler provides a central event bus for cross-Butler updates. When one Butler changes data (e.g., new expense, new reminder), others can subscribe and react—enabling automation and consistent state without tight coupling. This allows Butlers to work together seamlessly without direct dependencies.
Base Event Object
Every event flowing through the pipeline adheres to a standardized base schema. This ensures consistency across the ecosystem and provides the required metadata for routing and auditing.
interface BaseEvent {
id: string; // Unique identifier for the event
type: string; // Broad category (e.g., 'transaction')
sub_type: string; // Specific action (e.g., 'created', 'updated')
application_id: string; // The Butler ID that emitted the event
owned_by: string; // User ID who owns the resource
created_by: string; // User ID who triggered the event
created_on: string; // ISO 8601 timestamp
updated_on: string; // ISO 8601 timestamp
updated_by: string; // User ID who last updated the resource
payload: any; // The actual data of the event
links?: EventLink[]; // Optional references to related entities
}Event Links
Events can optionally include links to connect related data across different Butlers. A link represents a lightweight reference to another entity in the ecosystem.
interface EventLink {
id: string; // Target entity ID
type: string; // Target entity type
sub_type: string; // Target entity sub-type
application_id: string; // The Butler ID where the target entity lives
}Subscribe to Events
Subscribe to events published by other Butlers or The Butler itself.
Basic Event Subscription
// Subscribe to events
const unsubscribe = await window.lifeButler.events.subscribe('transaction.created', (event) => {
const { transaction, butlerId } = event;
// Handle new transaction
if (transaction.category === 'groceries') {
updateGroceryBudget(transaction.amount);
}
});
// Subscribe with filters
const unsubscribe = await window.lifeButler.events.subscribe('transaction.created', (event) => {
// Handle event
}, {
filter: (event) => event.transaction.category === 'groceries'
});Subscribe to Multiple Events
// Subscribe to multiple event types
const unsubscribe = await window.lifeButler.events.subscribeMany([
'transaction.created',
'transaction.updated',
'transaction.deleted'
], (event) => {
// Handle any of these events
refreshExpenseList();
});
// Unsubscribe when done
unsubscribe();Publish Events
Publish events when your Butler performs actions or changes data. Other Butlers can subscribe to these events.
Publish Event
// Publish event
await window.lifeButler.events.publish('expense.created', {
expenseId: 'exp-123',
amount: 25.50,
category: 'groceries',
date: '2024-01-15',
butlerId: 'my-butler'
});
// Publish with metadata
await window.lifeButler.events.publish('expense.created', {
expenseId: 'exp-123',
amount: 25.50,
category: 'groceries',
date: '2024-01-15',
butlerId: 'my-butler',
metadata: {
source: 'manual-entry',
linkedContactId: 'contact-123'
}
});Event Types
Common event types used across the platform. Your Butler can publish custom events or subscribe to platform events.
Data Events
entity.created- Entity created in any Butlerentity.updated- Entity updated in any Butlerentity.deleted- Entity deleted in any Butlertransaction.created- Transaction createdcontact.created- Contact created
Custom Events
Your Butler can publish custom events with any event type. Use namespaced event types (e.g., my-butler.expense.processed) to avoid conflicts.
Event-Driven Automation
Use events to enable automation workflows that span multiple Butlers.
// Example: When expense is created, update budget
window.lifeButler.events.subscribe('transaction.created', async (event) => {
const { transaction } = event;
if (transaction.category === 'groceries') {
// Update grocery budget
const budget = await window.lifeButler.data.get('Budget', 'grocery-budget');
const updated = await window.lifeButler.data.update('Budget', budget.id, {
spent: budget.spent + transaction.amount
});
// Publish budget update event
await window.lifeButler.events.publish('budget.updated', {
budgetId: budget.id,
category: 'groceries',
spent: updated.spent
});
}
});
// Example: When contact is created, link to expenses
window.lifeButler.events.subscribe('contact.created', async (event) => {
const { contact } = event;
// Find related expenses
const expenses = await window.lifeButler.data.query('Transaction', {
where: { merchant: contact.name }
});
// Link expenses to contact
for (const expense of expenses) {
await window.lifeButler.data.link('Transaction', expense.id, 'Contact', contact.id);
}
});Requirements
- Publish events: Publish events when your Butler performs actions or changes data that other Butlers might care about.
- Subscribe to events: Subscribe to events from other Butlers to enable cross-Butler workflows.
- Use namespaced events: Use namespaced event types (e.g.,
my-butler.event-name) for custom events to avoid conflicts. - Clean up subscriptions: Unsubscribe from events when your Butler unmounts or no longer needs the subscription.
- Handle errors: Handle errors in event handlers gracefully to prevent breaking other Butlers.