-
No labels found.
+
No {getFolderLabel()} found.
Try a different search or account.
@@ -371,7 +395,7 @@ export function FolderSelector({
{folders.length > 0 && (
- Labels
+ {getFolderLabel().charAt(0).toUpperCase() + getFolderLabel().slice(1)}
{folders.map((folder) => (
- Connect Gmail account
+ Connect {getProviderName()} account
)}
-
- {/* Add another account option */}
- {/* {credentials.length > 0 && (
-
-
-
- Connect Another Account
-
-
-
- )} */}
@@ -421,7 +434,7 @@ export function FolderSelector({
isOpen={showOAuthModal}
onClose={() => setShowOAuthModal(false)}
provider={provider}
- toolName='Gmail'
+ toolName={getProviderName()}
requiredScopes={requiredScopes}
serviceId={getServiceId()}
/>
diff --git a/apps/sim/blocks/blocks/outlook.ts b/apps/sim/blocks/blocks/outlook.ts
new file mode 100644
index 00000000000..4c2c8247392
--- /dev/null
+++ b/apps/sim/blocks/blocks/outlook.ts
@@ -0,0 +1,150 @@
+import { OutlookIcon } from '@/components/icons'
+import type {
+ OutlookDraftResponse,
+ OutlookReadResponse,
+ OutlookSendResponse,
+} from '@/tools/outlook/types'
+import type { BlockConfig } from '../types'
+
+export const OutlookBlock: BlockConfig<
+ OutlookReadResponse | OutlookSendResponse | OutlookDraftResponse
+> = {
+ type: 'outlook',
+ name: 'Outlook',
+ description: 'Access Outlook',
+ longDescription:
+ 'Integrate Outlook functionality to read, draft, andsend email messages within your workflow. Automate email communications and process email content using OAuth authentication.',
+ docsLink: 'https://docs.simstudio.ai/tools/outlook',
+ category: 'tools',
+ bgColor: '#E0E0E0',
+ icon: OutlookIcon,
+ subBlocks: [
+ // Operation selector
+ {
+ id: 'operation',
+ title: 'Operation',
+ type: 'dropdown',
+ layout: 'full',
+ options: [
+ { label: 'Send Email', id: 'send_outlook' },
+ { label: 'Draft Email', id: 'draft_outlook' },
+ { label: 'Read Email', id: 'read_outlook' },
+ ],
+ },
+ // Gmail Credentials
+ {
+ id: 'credential',
+ title: 'Microsoft Account',
+ type: 'oauth-input',
+ layout: 'full',
+ provider: 'outlook',
+ serviceId: 'outlook',
+ requiredScopes: [
+ 'Mail.ReadWrite',
+ 'Mail.ReadBasic',
+ 'Mail.Read',
+ 'Mail.Send',
+ 'offline_access',
+ 'openid',
+ 'profile',
+ 'email',
+ ],
+ placeholder: 'Select Microsoft account',
+ },
+ // Send Email Fields
+ {
+ id: 'to',
+ title: 'To',
+ type: 'short-input',
+ layout: 'full',
+ placeholder: 'Recipient email address',
+ condition: { field: 'operation', value: ['send_outlook', 'draft_outlook'] },
+ },
+ {
+ id: 'subject',
+ title: 'Subject',
+ type: 'short-input',
+ layout: 'full',
+ placeholder: 'Email subject',
+ condition: { field: 'operation', value: ['send_outlook', 'draft_outlook'] },
+ },
+ {
+ id: 'body',
+ title: 'Body',
+ type: 'long-input',
+ layout: 'full',
+ placeholder: 'Email content',
+ condition: { field: 'operation', value: ['send_outlook', 'draft_outlook'] },
+ },
+ // Read Email Fields - Add folder selector
+ {
+ id: 'folder',
+ title: 'Folder',
+ type: 'folder-selector',
+ layout: 'full',
+ provider: 'outlook',
+ serviceId: 'outlook',
+ requiredScopes: ['Mail.ReadWrite', 'Mail.ReadBasic', 'Mail.Read'],
+ placeholder: 'Select Outlook folder',
+ condition: { field: 'operation', value: 'read_outlook' },
+ },
+ {
+ id: 'maxResults',
+ title: 'Number of Emails',
+ type: 'short-input',
+ layout: 'full',
+ placeholder: 'Number of emails to retrieve (default: 1, max: 10)',
+ condition: { field: 'operation', value: 'read_outlook' },
+ },
+ ],
+ tools: {
+ access: ['outlook_send', 'outlook_draft', 'outlook_read'],
+ config: {
+ tool: (params) => {
+ switch (params.operation) {
+ case 'send_outlook':
+ return 'outlook_send'
+ case 'read_outlook':
+ return 'outlook_read'
+ case 'draft_outlook':
+ return 'outlook_draft'
+ default:
+ throw new Error(`Invalid Outlook operation: ${params.operation}`)
+ }
+ },
+ params: (params) => {
+ // Pass the credential directly from the credential field
+ const { credential, ...rest } = params
+
+ // Set default folder to INBOX if not specified
+ if (rest.operation === 'read_outlook' && !rest.folder) {
+ rest.folder = 'INBOX'
+ }
+
+ return {
+ ...rest,
+ credential, // Keep the credential parameter
+ }
+ },
+ },
+ },
+ inputs: {
+ operation: { type: 'string', required: true },
+ credential: { type: 'string', required: true },
+ // Send operation inputs
+ to: { type: 'string', required: false },
+ subject: { type: 'string', required: false },
+ body: { type: 'string', required: false },
+ // Read operation inputs
+ folder: { type: 'string', required: false },
+ maxResults: { type: 'number', required: false },
+ },
+ outputs: {
+ response: {
+ type: {
+ message: 'string',
+ results: 'json',
+ },
+ },
+ },
+}
diff --git a/apps/sim/blocks/registry.ts b/apps/sim/blocks/registry.ts
index 86f698af842..4571e9910cc 100644
--- a/apps/sim/blocks/registry.ts
+++ b/apps/sim/blocks/registry.ts
@@ -36,6 +36,7 @@ import { MicrosoftTeamsBlock } from './blocks/microsoft_teams'
import { MistralParseBlock } from './blocks/mistral_parse'
import { NotionBlock } from './blocks/notion'
import { OpenAIBlock } from './blocks/openai'
+import { OutlookBlock } from './blocks/outlook'
import { PerplexityBlock } from './blocks/perplexity'
import { PineconeBlock } from './blocks/pinecone'
import { RedditBlock } from './blocks/reddit'
@@ -92,6 +93,7 @@ export const registry: Record
= {
mistral_parse: MistralParseBlock,
notion: NotionBlock,
openai: OpenAIBlock,
+ outlook: OutlookBlock,
perplexity: PerplexityBlock,
pinecone: PineconeBlock,
reddit: RedditBlock,
diff --git a/apps/sim/components/icons.tsx b/apps/sim/components/icons.tsx
index 0d0b8e26c99..66f4568983b 100644
--- a/apps/sim/components/icons.tsx
+++ b/apps/sim/components/icons.tsx
@@ -2556,3 +2556,118 @@ export function MicrosoftTeamsIcon(props: SVGProps) {
)
}
+
+export function OutlookIcon(props: SVGProps) {
+ return (
+
+ )
+}
diff --git a/apps/sim/lib/auth.ts b/apps/sim/lib/auth.ts
index f6c3d6951a6..2963b22611c 100644
--- a/apps/sim/lib/auth.ts
+++ b/apps/sim/lib/auth.ts
@@ -397,6 +397,31 @@ export const auth = betterAuth({
redirectURI: `${env.NEXT_PUBLIC_APP_URL}/api/auth/oauth2/callback/microsoft-teams`,
},
+ {
+ providerId: 'outlook',
+ clientId: env.MICROSOFT_CLIENT_ID as string,
+ clientSecret: env.MICROSOFT_CLIENT_SECRET as string,
+ authorizationUrl: 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize',
+ tokenUrl: 'https://login.microsoftonline.com/common/oauth2/v2.0/token',
+ userInfoUrl: 'https://graph.microsoft.com/v1.0/me',
+ scopes: [
+ 'openid',
+ 'profile',
+ 'email',
+ 'Mail.ReadWrite',
+ 'Mail.ReadBasic',
+ 'Mail.Read',
+ 'Mail.Send',
+ 'offline_access',
+ ],
+ responseType: 'code',
+ accessType: 'offline',
+ authentication: 'basic',
+ prompt: 'consent',
+ pkce: true,
+ redirectURI: `${env.NEXT_PUBLIC_APP_URL}/api/auth/oauth2/callback/outlook`,
+ },
+
// Supabase provider
{
providerId: 'supabase',
diff --git a/apps/sim/lib/oauth.ts b/apps/sim/lib/oauth.ts
index 1262e68dd4d..ba295661b05 100644
--- a/apps/sim/lib/oauth.ts
+++ b/apps/sim/lib/oauth.ts
@@ -14,6 +14,7 @@ import {
MicrosoftIcon,
MicrosoftTeamsIcon,
NotionIcon,
+ OutlookIcon,
SupabaseIcon,
xIcon,
} from '@/components/icons'
@@ -51,6 +52,7 @@ export type OAuthService =
| 'jira'
| 'discord'
| 'microsoft-teams'
+ | 'outlook'
// Define the interface for OAuth provider configuration
export interface OAuthProviderConfig {
id: OAuthProvider
@@ -163,6 +165,24 @@ export const OAUTH_PROVIDERS: Record = {
'offline_access',
],
},
+ outlook: {
+ id: 'outlook',
+ name: 'Outlook',
+ description: 'Connect to Outlook and manage emails.',
+ providerId: 'outlook',
+ icon: (props) => OutlookIcon(props),
+ baseProviderIcon: (props) => MicrosoftIcon(props),
+ scopes: [
+ 'openid',
+ 'profile',
+ 'email',
+ 'Mail.ReadWrite',
+ 'Mail.ReadBasic',
+ 'Mail.Read',
+ 'Mail.Send',
+ 'offline_access',
+ ],
+ },
},
defaultService: 'microsoft',
},
@@ -356,6 +376,8 @@ export function getServiceIdFromScopes(provider: OAuthProvider, scopes: string[]
}
} else if (provider === 'microsoft-teams') {
return 'microsoft-teams'
+ } else if (provider === 'outlook') {
+ return 'outlook'
} else if (provider === 'github') {
return 'github'
} else if (provider === 'supabase') {
@@ -412,6 +434,14 @@ export interface ProviderConfig {
* This is a server-safe utility that can be used in both client and server code
*/
export function parseProvider(provider: OAuthProvider): ProviderConfig {
+ // Handle special cases first
+ if (provider === 'outlook') {
+ return {
+ baseProvider: 'microsoft',
+ featureType: 'outlook',
+ }
+ }
+
// Handle compound providers (e.g., 'google-email' -> { baseProvider: 'google', featureType: 'email' })
const [base, feature] = provider.split('-')
@@ -506,6 +536,11 @@ export async function refreshOAuthToken(
clientId = env.MICROSOFT_CLIENT_ID
clientSecret = env.MICROSOFT_CLIENT_SECRET
break
+ case 'outlook':
+ tokenEndpoint = 'https://login.microsoftonline.com/common/oauth2/v2.0/token'
+ clientId = env.MICROSOFT_CLIENT_ID
+ clientSecret = env.MICROSOFT_CLIENT_SECRET
+ break
default:
throw new Error(`Unsupported provider: ${provider}`)
}
diff --git a/apps/sim/tools/outlook/draft.ts b/apps/sim/tools/outlook/draft.ts
new file mode 100644
index 00000000000..677ef488f0e
--- /dev/null
+++ b/apps/sim/tools/outlook/draft.ts
@@ -0,0 +1,112 @@
+import type { ToolConfig } from '../types'
+import type { OutlookDraftParams, OutlookDraftResponse } from './types'
+
+export const outlookDraftTool: ToolConfig = {
+ id: 'outlook_draft',
+ name: 'Outlook Draft',
+ description: 'Draft emails using Outlook',
+ version: '1.0.0',
+
+ oauth: {
+ required: true,
+ provider: 'outlook',
+ },
+
+ params: {
+ accessToken: {
+ type: 'string',
+ required: true,
+ description: 'Access token for Outlook API',
+ },
+ to: {
+ type: 'string',
+ required: true,
+ description: 'Recipient email address',
+ },
+ subject: {
+ type: 'string',
+ required: true,
+ description: 'Email subject',
+ },
+ body: {
+ type: 'string',
+ required: true,
+ description: 'Email body content',
+ },
+ },
+
+ request: {
+ url: (params) => {
+ return `https://graph.microsoft.com/v1.0/me/messages`
+ },
+ method: 'POST',
+ headers: (params) => {
+ // Validate access token
+ if (!params.accessToken) {
+ throw new Error('Access token is required')
+ }
+
+ return {
+ Authorization: `Bearer ${params.accessToken}`,
+ 'Content-Type': 'application/json',
+ }
+ },
+ body: (params: OutlookDraftParams): Record => {
+ return {
+ subject: params.subject,
+ body: {
+ contentType: 'Text',
+ content: params.body,
+ },
+ toRecipients: [
+ {
+ emailAddress: {
+ address: params.to,
+ },
+ },
+ ],
+ }
+ },
+ },
+ transformResponse: async (response) => {
+ if (!response.ok) {
+ let errorData
+ try {
+ errorData = await response.json()
+ } catch {
+ throw new Error('Failed to draft email')
+ }
+ throw new Error(errorData.error?.message || 'Failed to draft email')
+ }
+
+ // Outlook draft API returns the created message object
+ const data = await response.json()
+
+ return {
+ success: true,
+ output: {
+ message: 'Email drafted successfully',
+ results: {
+ id: data.id,
+ subject: data.subject,
+ status: 'drafted',
+ timestamp: new Date().toISOString(),
+ },
+ },
+ }
+ },
+
+ transformError: (error) => {
+ // Handle Outlook API error format
+ if (error.error?.message) {
+ if (error.error.message.includes('invalid authentication credentials')) {
+ return 'Invalid or expired access token. Please reauthenticate.'
+ }
+ if (error.error.message.includes('quota')) {
+ return 'Outlook API quota exceeded. Please try again later.'
+ }
+ return error.error.message
+ }
+ return error.message || 'An unexpected error occurred while drafting email'
+ },
+}
diff --git a/apps/sim/tools/outlook/index.ts b/apps/sim/tools/outlook/index.ts
new file mode 100644
index 00000000000..6cd4ed142fa
--- /dev/null
+++ b/apps/sim/tools/outlook/index.ts
@@ -0,0 +1,5 @@
+import { outlookDraftTool } from './draft'
+import { outlookReadTool } from './read'
+import { outlookSendTool } from './send'
+
+export { outlookDraftTool, outlookReadTool, outlookSendTool }
diff --git a/apps/sim/tools/outlook/read.ts b/apps/sim/tools/outlook/read.ts
new file mode 100644
index 00000000000..3830bb3b8a3
--- /dev/null
+++ b/apps/sim/tools/outlook/read.ts
@@ -0,0 +1,146 @@
+import type { ToolConfig } from '../types'
+import type {
+ CleanedOutlookMessage,
+ OutlookMessage,
+ OutlookMessagesResponse,
+ OutlookReadParams,
+ OutlookReadResponse,
+} from './types'
+
+export const outlookReadTool: ToolConfig = {
+ id: 'outlook_read',
+ name: 'Outlook Read',
+ description: 'Read emails from Outlook',
+ version: '1.0.0',
+
+ oauth: {
+ required: true,
+ provider: 'outlook',
+ },
+ params: {
+ accessToken: {
+ type: 'string',
+ required: true,
+ description: 'OAuth access token for Outlook',
+ },
+ folder: {
+ type: 'string',
+ required: false,
+ description: 'Folder ID to read emails from (default: Inbox)',
+ },
+ maxResults: {
+ type: 'number',
+ required: false,
+ description: 'Maximum number of emails to retrieve (default: 1, max: 10)',
+ },
+ },
+ request: {
+ url: (params) => {
+ // Set max results (default to 1 for simplicity, max 10) with no negative values
+ const maxResults = params.maxResults
+ ? Math.max(1, Math.min(Math.abs(params.maxResults), 10))
+ : 1
+
+ // If folder is provided, read from that specific folder
+ if (params.folder) {
+ return `https://graph.microsoft.com/v1.0/me/mailFolders/${params.folder}/messages?$top=${maxResults}&$orderby=createdDateTime desc`
+ }
+
+ // Otherwise fetch from all messages (default behavior)
+ return `https://graph.microsoft.com/v1.0/me/messages?$top=${maxResults}&$orderby=createdDateTime desc`
+ },
+ method: 'GET',
+ headers: (params) => {
+ // Validate access token
+ if (!params.accessToken) {
+ throw new Error('Access token is required')
+ }
+
+ return {
+ Authorization: `Bearer ${params.accessToken}`,
+ }
+ },
+ },
+ transformResponse: async (response: Response) => {
+ if (!response.ok) {
+ const errorText = await response.text()
+ throw new Error(`Failed to read Outlook mail: ${errorText}`)
+ }
+
+ const data: OutlookMessagesResponse = await response.json()
+
+ // Microsoft Graph API returns messages in a 'value' array
+ const messages = data.value || []
+
+ if (messages.length === 0) {
+ return {
+ success: true,
+ output: {
+ message: 'No mail found.',
+ results: [],
+ },
+ }
+ }
+
+ // Clean up the message data to only include essential fields
+ const cleanedMessages: CleanedOutlookMessage[] = messages.map((message: OutlookMessage) => ({
+ id: message.id,
+ subject: message.subject,
+ bodyPreview: message.bodyPreview,
+ body: {
+ contentType: message.body?.contentType,
+ content: message.body?.content,
+ },
+ sender: {
+ name: message.sender?.emailAddress?.name,
+ address: message.sender?.emailAddress?.address,
+ },
+ from: {
+ name: message.from?.emailAddress?.name,
+ address: message.from?.emailAddress?.address,
+ },
+ toRecipients:
+ message.toRecipients?.map((recipient) => ({
+ name: recipient.emailAddress?.name,
+ address: recipient.emailAddress?.address,
+ })) || [],
+ ccRecipients:
+ message.ccRecipients?.map((recipient) => ({
+ name: recipient.emailAddress?.name,
+ address: recipient.emailAddress?.address,
+ })) || [],
+ receivedDateTime: message.receivedDateTime,
+ sentDateTime: message.sentDateTime,
+ hasAttachments: message.hasAttachments,
+ isRead: message.isRead,
+ importance: message.importance,
+ }))
+
+ return {
+ success: true,
+ output: {
+ message: `Successfully read ${cleanedMessages.length} email(s).`,
+ results: cleanedMessages,
+ },
+ }
+ },
+ transformError: (error) => {
+ // If it's an Error instance with a message, use that
+ if (error instanceof Error) {
+ return error.message
+ }
+
+ // If it's an object with an error or message property
+ if (typeof error === 'object' && error !== null) {
+ if (error.error) {
+ return typeof error.error === 'string' ? error.error : JSON.stringify(error.error)
+ }
+ if (error.message) {
+ return error.message
+ }
+ }
+
+ // Default fallback message
+ return 'An error occurred while reading Outlook email'
+ },
+}
diff --git a/apps/sim/tools/outlook/send.ts b/apps/sim/tools/outlook/send.ts
new file mode 100644
index 00000000000..f7ed643251a
--- /dev/null
+++ b/apps/sim/tools/outlook/send.ts
@@ -0,0 +1,111 @@
+import type { ToolConfig } from '../types'
+import type { OutlookSendParams, OutlookSendResponse } from './types'
+
+export const outlookSendTool: ToolConfig = {
+ id: 'outlook_send',
+ name: 'Outlook Send',
+ description: 'Send emails using Outlook',
+ version: '1.0.0',
+
+ oauth: {
+ required: true,
+ provider: 'outlook',
+ },
+
+ params: {
+ accessToken: {
+ type: 'string',
+ required: true,
+ description: 'Access token for Outlook API',
+ },
+ to: {
+ type: 'string',
+ required: true,
+ description: 'Recipient email address',
+ },
+ subject: {
+ type: 'string',
+ required: true,
+ description: 'Email subject',
+ },
+ body: {
+ type: 'string',
+ required: true,
+ description: 'Email body content',
+ },
+ },
+
+ request: {
+ url: (params) => {
+ return `https://graph.microsoft.com/v1.0/me/sendMail`
+ },
+ method: 'POST',
+ headers: (params) => {
+ // Validate access token
+ if (!params.accessToken) {
+ throw new Error('Access token is required')
+ }
+
+ return {
+ Authorization: `Bearer ${params.accessToken}`,
+ 'Content-Type': 'application/json',
+ }
+ },
+ body: (params: OutlookSendParams): Record => {
+ return {
+ message: {
+ subject: params.subject,
+ body: {
+ contentType: 'Text',
+ content: params.body,
+ },
+ toRecipients: [
+ {
+ emailAddress: {
+ address: params.to,
+ },
+ },
+ ],
+ },
+ saveToSentItems: true,
+ }
+ },
+ },
+ transformResponse: async (response) => {
+ if (!response.ok) {
+ let errorData
+ try {
+ errorData = await response.json()
+ } catch {
+ throw new Error('Failed to send email')
+ }
+ throw new Error(errorData.error?.message || 'Failed to send email')
+ }
+
+ // Outlook sendMail API returns empty body on success
+ return {
+ success: true,
+ output: {
+ message: 'Email sent successfully',
+ results: {
+ status: 'sent',
+ timestamp: new Date().toISOString(),
+ },
+ },
+ }
+ },
+
+ transformError: (error) => {
+ // Handle Outlook API error format
+ if (error.error?.message) {
+ if (error.error.message.includes('invalid authentication credentials')) {
+ return 'Invalid or expired access token. Please reauthenticate.'
+ }
+ if (error.error.message.includes('quota')) {
+ return 'Outlook API quota exceeded. Please try again later.'
+ }
+ return error.error.message
+ }
+ return error.message || 'An unexpected error occurred while sending email'
+ },
+}
diff --git a/apps/sim/tools/outlook/types.ts b/apps/sim/tools/outlook/types.ts
new file mode 100644
index 00000000000..5ad7cae54f4
--- /dev/null
+++ b/apps/sim/tools/outlook/types.ts
@@ -0,0 +1,129 @@
+import type { ToolResponse } from '../types'
+
+export interface OutlookSendParams {
+ accessToken: string
+ to: string
+ subject: string
+ body: string
+}
+
+export interface OutlookSendResponse extends ToolResponse {
+ output: {
+ message: string
+ results: any
+ }
+}
+
+export interface OutlookReadParams {
+ accessToken: string
+ folder: string
+ maxResults: number
+ messageId?: string
+}
+
+export interface OutlookReadResponse extends ToolResponse {
+ output: {
+ message: string
+ results: CleanedOutlookMessage[]
+ }
+}
+
+export interface OutlookDraftParams {
+ accessToken: string
+ to: string
+ subject: string
+ body: string
+}
+
+export interface OutlookDraftResponse extends ToolResponse {
+ output: {
+ message: string
+ results: any
+ }
+}
+
+// Outlook API response interfaces
+export interface OutlookEmailAddress {
+ name?: string
+ address: string
+}
+
+export interface OutlookRecipient {
+ emailAddress: OutlookEmailAddress
+}
+
+export interface OutlookMessageBody {
+ contentType?: string
+ content?: string
+}
+
+export interface OutlookMessage {
+ id: string
+ subject?: string
+ bodyPreview?: string
+ body?: OutlookMessageBody
+ sender?: OutlookRecipient
+ from?: OutlookRecipient
+ toRecipients?: OutlookRecipient[]
+ ccRecipients?: OutlookRecipient[]
+ bccRecipients?: OutlookRecipient[]
+ receivedDateTime?: string
+ sentDateTime?: string
+ hasAttachments?: boolean
+ isRead?: boolean
+ importance?: string
+ // Add other common fields
+ '@odata.etag'?: string
+ createdDateTime?: string
+ lastModifiedDateTime?: string
+ changeKey?: string
+ categories?: string[]
+ internetMessageId?: string
+ parentFolderId?: string
+ conversationId?: string
+ conversationIndex?: string
+ isDeliveryReceiptRequested?: boolean | null
+ isReadReceiptRequested?: boolean
+ isDraft?: boolean
+ webLink?: string
+ inferenceClassification?: string
+ replyTo?: OutlookRecipient[]
+}
+
+export interface OutlookMessagesResponse {
+ '@odata.context'?: string
+ '@odata.nextLink'?: string
+ value: OutlookMessage[]
+}
+
+// Cleaned message interface for our response
+export interface CleanedOutlookMessage {
+ id: string
+ subject?: string
+ bodyPreview?: string
+ body?: {
+ contentType?: string
+ content?: string
+ }
+ sender?: {
+ name?: string
+ address?: string
+ }
+ from?: {
+ name?: string
+ address?: string
+ }
+ toRecipients: Array<{
+ name?: string
+ address?: string
+ }>
+ ccRecipients: Array<{
+ name?: string
+ address?: string
+ }>
+ receivedDateTime?: string
+ sentDateTime?: string
+ hasAttachments?: boolean
+ isRead?: boolean
+ importance?: string
+}
diff --git a/apps/sim/tools/registry.ts b/apps/sim/tools/registry.ts
index 83578a844df..46b6aeebf4d 100644
--- a/apps/sim/tools/registry.ts
+++ b/apps/sim/tools/registry.ts
@@ -57,6 +57,7 @@ import {
import { mistralParserTool } from './mistral'
import { notionReadTool, notionWriteTool } from './notion'
import { imageTool, embeddingsTool as openAIEmbeddings } from './openai'
+import { outlookDraftTool, outlookReadTool, outlookSendTool } from './outlook'
import { perplexityChatTool } from './perplexity'
import {
pineconeFetchTool,
@@ -183,4 +184,7 @@ export const tools: Record = {
microsoft_teams_write_chat: microsoftTeamsWriteChatTool,
microsoft_teams_read_channel: microsoftTeamsReadChannelTool,
microsoft_teams_write_channel: microsoftTeamsWriteChannelTool,
+ outlook_read: outlookReadTool,
+ outlook_send: outlookSendTool,
+ outlook_draft: outlookDraftTool,
}