How Macro Implements a Unified Inbox to Aggregate Emails, Messages, and Tasks
Macro's unified inbox is a logical database view that normalizes emails, message mentions, and tasks into a chronologically ordered feed accessible via a single SQL query and exposed through both AI tools and a React-based web interface.
The unified inbox in the macro-inc/macro repository consolidates disparate communication streams into a single, queryable data model. By leveraging PostgreSQL views and a shared schema, the system aggregates Gmail notifications, chat mentions, and task items without requiring separate UI implementations for each source.
Data Aggregation Layer
The foundation of Macro’s unified inbox relies on normalizing disparate sources into a common entity model called InboxItem. This abstraction allows the system to treat emails, mentions, and tasks as interchangeable records within the same queryable dataset.
Gmail Integration
Incoming email data enters the system through Gmail push notifications. In crates/models_email/src/gmail/inbox_sync.rs, the service parses GmailInboxSyncPayload structures and produces InboxSyncOperation records. These operations translate into InboxSyncPubsubMessage entries that are ultimately persisted to the inbox_items table with entity_type set to "email".
Shared Database Schema
The central persistence layer resides in crates/macro_db_client/src/shared_inbox.rs, which defines two critical tables:
inbox_items– Stores unified representations of emails and tasksentity_mentions– Captures message mentions linking source messages to target users
This schema design ensures that all inbox-compatible entities share a consistent structure, enabling polymorphic queries across source types.
Business Logic Layer
The business logic determines which items belong to a user’s inbox and establishes the chronological ordering rules. This layer transforms raw database records into a consumable unified view.
Database Views for Unified Access
Rather than querying multiple tables directly, Macro employs PostgreSQL views to flatten the data. In crates/email/src/outbound/email_pg_repo/preview_views/new_inbox.rs and other_inbox.rs, the system creates database views that join email tables with entity_mentions and task tables. These views expose a single SELECT interface that returns a unified result set ordered by created_at in descending order.
AI Tool Integration
The unified inbox concept is explicitly defined for AI consumption in crates/prompt/src/about_macro.rs. The ListEntities tool understands the unified inbox as "a chronologically ordered list of recent emails, mentions, and tasks" and accepts a filter parameter with enum values ["all", "email", "mention", "task"]. This allows AI agents to query the same unified feed available to human users.
Presentation Layer
The frontend consumes the unified inbox through a set of TypeScript utilities and React components that translate user interactions into API calls.
Frontend Filtering Logic
In apps/web/src/features/next-soup/filters/inbox-query-filters.ts, the application builds query parameters (inbox, mention, task) used when fetching the feed. The companion file inbox-filters.ts provides UI controls—including an inbox selector and picker—that allow users to toggle the unified view on or off.
Visual Components
The unified inbox is represented throughout the interface by components defined in apps/web/src/components/icon/wide-inbox.tsx and wide-inbox.svg, providing consistent visual identification of the aggregated feed.
Data Flow Architecture
The complete data pipeline follows a unidirectional flow from external services to the user interface:
- Email ingestion – Gmail pushes notifications →
inbox_sync.rscreatesInboxItemrecords withentity_type = "email" - Mention processing – Chat services insert rows into
entity_mentionswith source type message and target user references - Task tracking – Task modifications insert rows into
inbox_itemswithentity_type = "task" - Unified querying – The
list_inboxestool inlist_inboxes.rsexecutes a SQL query against thenew_inboxview, UNION-ing the three source tables and sorting bycreated_at - Frontend rendering – TypeScript utilities fetch the unified list and render each item with appropriate type badges (email, mention, task)
This architecture means adding a new source type—such as calendar events—requires only inserting rows into inbox_items with a new entity_type value. The unified inbox automatically includes these items without requiring changes to the UI components or AI tool definitions.
Code Examples
Fetching the Unified Inbox (Rust Backend)
The list_inboxes.rs file implements the primary query interface:
// In crates/email/src/inbound/toolset/list_inboxes.rs
use sqlx::query_as;
#[derive(Debug, sqlx::FromRow)]
pub struct UnifiedInboxItem {
pub id: uuid::Uuid,
pub entity_type: String, // "email" | "mention" | "task"
pub title: String,
pub snippet: Option<String>,
pub created_at: chrono::NaiveDateTime,
}
// Returns the unified inbox for a given user
pub async fn list_unified_inbox(
db: &sqlx::PgPool,
user_id: uuid::Uuid,
) -> Result<Vec<UnifiedInboxItem>, sqlx::Error> {
// The view `new_inbox` is defined in `new_inbox.rs`
let rows = query_as::<_, UnifiedInboxItem>(
"SELECT * FROM new_inbox WHERE user_id = $1 ORDER BY created_at DESC"
)
.bind(user_id)
.fetch_all(db)
.await?;
Ok(rows)
}
AI Tool Definition
The unified inbox specification for AI tools resides in about_macro.rs:
// In crates/prompt/src/about_macro.rs (excerpt)
pub const LIST_INBOX_TOOL: &str = r#"
{
"name": "ListEntities",
"description": "Returns the unified inbox – a chronologically ordered list of recent emails, mentions, and tasks.",
"parameters": {
"type": "object",
"properties": {
"user_id": { "type": "string", "format": "uuid" },
"filter": { "type": "string", "enum": ["all","email","mention","task"] }
},
"required": ["user_id"]
}
}
"#;
Frontend Request Handler (TypeScript)
The web client queries the unified inbox through the filter utilities:
// apps/web/src/features/next-soup/filters/inbox-query-filters.ts
export async function fetchUnifiedInbox(userId: string, filter = "all") {
const resp = await fetch(`/api/v1/inbox?user_id=${userId}&filter=${filter}`);
if (!resp.ok) throw new Error("Failed to load inbox");
return resp.json() as Promise<InboxItem[]>;
}
Summary
- Macro's unified inbox aggregates emails, message mentions, and tasks into a single chronological feed using a PostgreSQL view that UNIONs multiple source tables.
- Data normalization occurs in
shared_inbox.rsandinbox_sync.rs, where disparate inputs become standardizedInboxItemrecords. - Query efficiency is achieved through database views defined in
new_inbox.rs, allowing a singleSELECTstatement to return all item types. - Extensibility is built into the design: new source types only require inserting rows with a unique
entity_typewithout modifying UI or AI layers. - Cross-platform consistency ensures both human users (via React components) and AI agents (via the ListEntities tool) consume identical unified inbox data.
Frequently Asked Questions
How does the unified inbox handle different data sources?
The unified inbox normalizes inputs through the inbox_items and entity_mentions tables defined in crates/macro_db_client/src/shared_inbox.rs. Each source—whether Gmail for emails, chat services for mentions, or task managers—inserts records with a distinct entity_type field. The PostgreSQL view new_inbox then UNIONs these tables, presenting a homogeneous interface for querying.
What makes Macro's unified inbox extensible to new source types?
The architecture relies on polymorphic database records rather than source-specific schemas. Because the inbox_items table uses a generic entity_type discriminator, adding calendar events or other sources requires only inserting rows with a new type identifier. The existing views in preview_views/new_inbox.rs, AI tool definitions in about_macro.rs, and frontend components automatically include new types without code changes.
How does the frontend filter the unified inbox by item type?
The TypeScript module apps/web/src/features/next-soup/filters/inbox-query-filters.ts constructs API requests with a filter parameter that accepts "all", "email", "mention", or "task" values. This parameter maps directly to the backend's SQL WHERE clause against the entity_type column, allowing users to toggle between viewing the complete unified feed or isolating specific communication channels.
Where is the unified inbox concept defined for AI interactions?
The AI tool specification resides in crates/prompt/src/about_macro.rs, which documents the ListEntities tool. This definition explains that the unified inbox represents "a chronologically ordered list of recent emails, mentions, and tasks" and exposes filtering capabilities. The tool implementation ultimately queries the same database views used by the web application, ensuring AI agents and human users see identical data.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →