# How Macro Implements a Unified Inbox to Aggregate Emails, Messages, and Tasks

> Discover how Macro's unified inbox aggregates emails messages and tasks into a single chronological feed using a logical database view and SQL query for seamless access.

- Repository: [Macro/macro](https://github.com/macro-inc/macro)
- Tags: how-to-guide
- Published: 2026-08-18

---

**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](https://github.com/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`](https://github.com/macro-inc/macro/blob/main/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`](https://github.com/macro-inc/macro/blob/main/crates/macro_db_client/src/shared_inbox.rs), which defines two critical tables:

- **`inbox_items`** – Stores unified representations of emails and tasks
- **`entity_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`](https://github.com/macro-inc/macro/blob/main/crates/email/src/outbound/email_pg_repo/preview_views/new_inbox.rs) and [`other_inbox.rs`](https://github.com/macro-inc/macro/blob/main/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`](https://github.com/macro-inc/macro/blob/main/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`](https://github.com/macro-inc/macro/blob/main/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`](https://github.com/macro-inc/macro/blob/main/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`](https://github.com/macro-inc/macro/blob/main/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:

1. **Email ingestion** – Gmail pushes notifications → [`inbox_sync.rs`](https://github.com/macro-inc/macro/blob/main/inbox_sync.rs) creates `InboxItem` records with `entity_type = "email"`
2. **Mention processing** – Chat services insert rows into `entity_mentions` with source type *message* and target user references
3. **Task tracking** – Task modifications insert rows into `inbox_items` with `entity_type = "task"`
4. **Unified querying** – The `list_inboxes` tool in [`list_inboxes.rs`](https://github.com/macro-inc/macro/blob/main/list_inboxes.rs) executes a SQL query against the `new_inbox` view, UNION-ing the three source tables and sorting by `created_at`
5. **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`](https://github.com/macro-inc/macro/blob/main/list_inboxes.rs) file implements the primary query interface:

```rust
// 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`](https://github.com/macro-inc/macro/blob/main/about_macro.rs):

```rust
// 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:

```typescript
// 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.rs`](https://github.com/macro-inc/macro/blob/main/shared_inbox.rs) and [`inbox_sync.rs`](https://github.com/macro-inc/macro/blob/main/inbox_sync.rs), where disparate inputs become standardized `InboxItem` records.
- **Query efficiency** is achieved through database views defined in [`new_inbox.rs`](https://github.com/macro-inc/macro/blob/main/new_inbox.rs), allowing a single `SELECT` statement to return all item types.
- **Extensibility** is built into the design: new source types only require inserting rows with a unique `entity_type` without 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`](https://github.com/macro-inc/macro/blob/main/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`](https://github.com/macro-inc/macro/blob/main/preview_views/new_inbox.rs), AI tool definitions in [`about_macro.rs`](https://github.com/macro-inc/macro/blob/main/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`](https://github.com/macro-inc/macro/blob/main/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`](https://github.com/macro-inc/macro/blob/main/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.