Documentation

Platform Architecture

How the application layers connect, how users authenticate, and how data flows through the system.

Application Layers

Clevermore is split into three layers, each deployed independently:

LayerTechnologyHosted On
Outlook TaskpaneReact (Next.js) rendered inside Outlook's iframeVercel
Discovery EndpointA small Next.js route that resolves which deployment a signed-in user belongs to. Holds no firm data and has no database.Vercel
Backend ServicesExpress server running all business logic, AI processing, and durable background jobsAzure Container Apps

The Outlook taskpane is a standard web application loaded by Outlook via a manifest file. On sign-in it calls the discovery endpoint once to learn which deployment it belongs to, then talks directly to that deployment’s Container App over HTTPS for the rest of the session.

Firm data never transits Vercel. This is the same in every deployment model. The Vercel layer serves the taskpane assets and answers one question — which backend a user belongs to. It has no database by design, and email content, extracted tasks, contacts, and knowledge entries are never sent to it. All firm data is handled by the deployment’s own Container App and stored in that deployment’s own PostgreSQL instance.

Authentication Model

All authentication flows through Microsoft Entra ID (Azure AD). Clevermore uses the OAuth 2.0 On-Behalf-Of (OBO) flow to obtain delegated tokens for accessing Microsoft Graph and other services.

Outlook SSO
User opens add-in. Outlook provides a bootstrap token via SSO — no login prompt.
Token Sent
Taskpane sends the bootstrap token to the Clevermore API.
OBO Exchange
API exchanges bootstrap token for a delegated access token via the OBO flow.
Graph + API
Delegated token used for Microsoft Graph calls and Clevermore backend auth.
SSO is seamless — users authenticate with their existing Microsoft 365 credentials. No separate Clevermore account is needed.

Add-in Permissions (Delegated)

PermissionPurpose
openid, profile, email, offline_accessStandard OpenID Connect sign-in, plus refresh tokens so users are not re-prompted every session.
User.ReadRead the signed-in user's own profile (display name, mail, ID).
Mail.ReadRead message bodies, subjects, and recipients to extract tasks, build catch-up summaries, and enrich contact profiles.
Mail.ReadWriteApply Outlook categories, toggle flags, and mark messages read/unread when the user accepts a Clevermore suggestion.
Mail.ReadWrite.SharedThe same operations on shared and delegated mailboxes (executive-assistant scenarios).
Mail.SendSend drafts the user has explicitly composed or approved.
Mail.Send.SharedSend from a delegated or shared mailbox the user holds send-as rights on.
MailboxFolder.ReadWriteEnumerate existing mail folders to map client matters onto the folders the firm already uses, and file a conversation when the user asks.
MailboxItem.ReadRead item-level metadata for reliable thread reconstruction across moves and re-files.
MailboxSettings.ReadWriteRead the user's time zone and signature, and maintain the mailbox master category list so applied categories render correctly.
Calendars.ReadWriteRead upcoming events for meeting context in summaries and tasks, and create entries when the user accepts a scheduling suggestion.
Contacts.ReadWriteRead Outlook contacts to seed contact profiles, and write back enriched details when the user accepts a suggestion.
People.Read, User.Read.All, User.ReadBasic.AllResolve email participants to display names and basic profile fields. The directory is never enumerated and extended attributes are never read.
Group.Read.AllResolve distribution-list and Microsoft 365 group display names so participant lists are readable rather than raw object IDs.
Files.ReadWrite.AppFolderRead and write only inside Clevermore's own folder in the user's OneDrive. Grants no access to any other file, folder, or SharePoint content.
OnlineMeetings.ReadWriteReserved for Teams meeting creation. Declared now so the feature can ship without a tenant-wide re-consent cycle — not called by any code path today.
Tasks.ReadWriteReserved for opt-in Microsoft To Do sync. Declared now for the same reason — not called by any code path today.

These permissions are delegated (not application-level), meaning they act on behalf of the signed-in user and can never exceed that user’s own mailbox access. Consent is granted once, tenant-wide, by a Global Administrator — the consent dialog displays every permission above, and nothing is requested at runtime outside that initial grant.

Full permissions policy. Each scope above, the least-privilege rationale behind it (for example why we take Files.ReadWrite.AppFolder rather than Files.ReadWrite.All), and the consent, revocation, and audit procedures are documented in the Microsoft Graph Permissions Policy. The app registration is defined as Bicep in source control, so the deployed set can be verified at any time with az ad app permission list or in the Entra ID portal.

Admin Portal Permissions

PermissionPurpose
openid, profile, email, User.ReadStandard OIDC login for admin users
User.Read.All (optional)Enables user directory search when adding managed users

The admin portal requires only standard OIDC scopes to sign in — no elevated permissions or admin consent ceremony is needed. User.Read.All is an optional enhancement that can be enabled incrementally from within the portal.

App Registrations

Clevermore uses two multi-tenant app registrations in Clevermore’s Azure AD tenant: one for the Outlook add-in (with OBO flow and Mail.Read) and one for the admin portal (OIDC login only). Both are multi-tenant, so no app registration needs to be created in the client’s tenant.

When a firm deploys the add-in via Integrated Apps, a service principal is created in their tenant automatically. The admin portal creates its own service principal on first admin sign-in.

Data Flow

When a user opens an email in Outlook, the following sequence occurs:

  1. Outlook provides the email’s metadata (sender, recipients, subject, date) to the taskpane via the Office.js API.
  2. The taskpane sends the email ID to the Clevermore API, which uses the delegated token to fetch the full email body from Microsoft Graph.
  3. The backend processes the email content through AI models (with zero data retention) to extract tasks, update contact profiles, and build wiki entries.
  4. Extracted data is stored in the tenant’s PostgreSQL database, isolated via row-level security policies.
  5. Real-time updates are pushed to all connected clients via Azure Web PubSub (WebSocket-based).

Deployment Resolution

In a multi-tenant environment, Clevermore needs to route each request to the correct backend infrastructure. This is handled by the deployment resolver:

  1. User authenticates → JWT tid claim identifies their Azure AD tenant.
  2. The resolver maps tenantId + orgId to the organization’s deployment config.
  3. The deployment config provides all infrastructure endpoints: PostgreSQL host, Redis prefix, Web PubSub endpoint, and Container App URL.

For firms with multiple offices (e.g., different Dentons offices), each office maps to its own organization and potentially its own deployment with fully isolated infrastructure.

Real-Time Updates

Clevermore uses Azure Web PubSub for real-time communication between the backend and connected Outlook clients. When the backend processes an email and extracts tasks, it publishes events to Web PubSub, which fans them out to all clients belonging to that user.

Web PubSub is configured with OAuth-only authentication (no access keys). Messages pass through without being stored — Web PubSub acts as a pure relay.

AI Processing

AI processing is handled by external providers (Google Vertex AI, OpenAI) configured with zero data retention. Email content is sent to the AI provider for analysis, and the response is processed immediately. No email content is stored by the AI provider, logged, or used for model training.

The AI provider is configurable per deployment, allowing firms to choose their preferred provider or use a private Azure OpenAI instance within their own tenant for maximum data sovereignty.

Outlook Performance

Clevermore is a Microsoft 365 web add-in. No Clevermore code runs inside outlook.exe; its taskpane is a separate WebView2 (Windows) or WKWebView (Mac) process managed by the Office host, visible under Outlook in Task Manager or Performance Monitor.

ConcernBehavior
Outlook launchWeb add-ins are not loaded at startup. The taskpane process starts when a user first opens it, so cold launch is unaffected.
MemoryBounded by the taskpane process, typically tens of MB, and reclaimed in full when it exits. Outlook’s own memory is unaffected.
CPU at idleEffectively zero. The taskpane is event-driven and never polls the mailbox; email is analyzed on the server after Exchange delivers it.

This is why Clevermore avoids the memory problems firms associate with VSTO add-ins. VSTO add-ins load into outlook.exe as in-process COM components, so a missed reference release pins memory until Outlook restarts. A web add-in reaches Outlook only through the sandboxed Office.js bridge, with no shared object references to leak, and if its taskpane ever crashed or hung, Outlook itself would keep running. Microsoft documents this model in the Office Add-ins overview.