Skip to main content

Microsoft Integration

ConsultFlow integrates deeply with Microsoft 365 services via Microsoft Graph API.

Supported Services​

ServiceCapabilityAPI
Azure ADAuthenticationOAuth 2.0
OutlookSend emailsMail.Send
Outlook CalendarCreate eventsCalendars.ReadWrite
Microsoft TeamsPost messagesChannelMessage.Send

Authentication​

Azure AD OAuth 2.0​

ConsultFlow uses delegated permissions, meaning actions are performed on behalf of the signed-in user.

Required Permissions:

PermissionTypePurpose
User.ReadDelegatedRead user profile
Mail.SendDelegatedSend emails
Calendars.ReadWriteDelegatedCreate calendar events
ChannelMessage.SendDelegatedPost to Teams
offline_accessDelegatedRefresh tokens

Setting Up Azure AD​

See Azure Setup Guide for detailed instructions.

Email Integration (Outlook)​

Sending Emails​

ConsultFlow sends emails through Outlook using the signed-in user's account.

Features:

  • HTML formatted emails
  • Multiple recipients
  • Pre-filled with MOM content

Example:

await graphClient.sendEmail(
"recipient@example.com",
"Meeting Minutes - Project Status",
"<h1>Meeting Minutes</h1><p>...</p>"
)

Email API Request​

POST /me/sendMail
{
"message": {
"subject": "Meeting Minutes",
"body": {
"contentType": "HTML",
"content": "<h1>Meeting Minutes</h1>..."
},
"toRecipients": [
{
"emailAddress": {
"address": "recipient@example.com"
}
}
]
}
}

Multiple Recipients​

Separate email addresses with commas in the UI:

john@example.com, sarah@example.com, mike@example.com

Calendar Integration (Outlook)​

Creating Events​

Schedule meetings directly to Outlook calendar.

Features:

  • Set title, start/end times
  • Add attendees (sends invites)
  • Include event body/description

Example:

await graphClient.createCalendarEvent(
"Follow-up Meeting",
"Discuss action items from previous meeting",
"2024-12-27T14:00:00",
"2024-12-27T15:00:00",
["john@example.com", "sarah@example.com"]
)

Calendar API Request​

POST /me/events
{
"subject": "Follow-up Meeting",
"body": {
"contentType": "HTML",
"content": "Meeting description..."
},
"start": {
"dateTime": "2024-12-27T14:00:00",
"timeZone": "UTC"
},
"end": {
"dateTime": "2024-12-27T15:00:00",
"timeZone": "UTC"
},
"attendees": [
{
"emailAddress": {
"address": "john@example.com"
},
"type": "required"
}
]
}

Time Zone Handling​

  • Events are created in UTC by default
  • Consider user's timezone for accurate scheduling
  • Use ISO 8601 format: YYYY-MM-DDTHH:MM:SS

Teams Integration​

Posting to Channels​

Share updates with your team via Teams channels.

Requirements:

  • Team ID
  • Channel ID
  • User must be a member of the team

Example:

await graphClient.postTeamsMessage(
"team-id-here",
"channel-id-here",
"Meeting minutes have been shared..."
)

Teams API Request​

POST /teams/{team-id}/channels/{channel-id}/messages
{
"body": {
"content": "<h1>Meeting Minutes</h1>..."
}
}

Finding Team and Channel IDs​

Via Graph Explorer:

  1. Go to Graph Explorer
  2. Sign in with your Microsoft account
  3. Run: GET https://graph.microsoft.com/v1.0/me/joinedTeams
  4. Find your team ID
  5. Run: GET https://graph.microsoft.com/v1.0/teams/{team-id}/channels
  6. Find your channel ID

IDs Look Like:

Team ID: 19:meeting_abc123...@thread.v2
Channel ID: 19:def456...@thread.tacv2

Graph Client Implementation​

// src/services/graph/graphClient.ts
import axios from 'axios'

export class GraphClient {
private client: AxiosInstance

constructor(accessToken: string) {
this.client = axios.create({
baseURL: 'https://graph.microsoft.com/v1.0',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
})
}

async sendEmail(to: string, subject: string, body: string) {
// Validate permissions first
const scopeCheck = checkRequiredScopes(this.accessToken, ['Mail.Send'])
if (!scopeCheck.hasAll) {
throw new Error(`Missing permissions: ${scopeCheck.missing.join(', ')}`)
}

await this.client.post('/me/sendMail', {
message: {
subject,
body: { contentType: 'HTML', content: body },
toRecipients: [{ emailAddress: { address: to } }],
},
})
}

// Similar methods for calendar and teams...
}

Error Handling​

Common Errors​

Error CodeDescriptionSolution
401UnauthorizedRe-authenticate, check token
403ForbiddenMissing permissions
404Not FoundInvalid ID (team, channel)
429ThrottledToo many requests, wait

Error Response Example​

{
"error": {
"code": "Authorization_RequestDenied",
"message": "Insufficient privileges to complete the operation."
}
}

Handling in Code​

try {
await graphClient.sendEmail(...)
} catch (error: any) {
if (error.response?.status === 401) {
// Token expired, refresh needed
} else if (error.response?.status === 403) {
// Missing permissions
}
throw error
}

Troubleshooting​

"Insufficient privileges"​

  1. Check permissions in Azure Portal
  2. Grant admin consent if required
  3. Re-authenticate to get new token

"Resource not found"​

  1. Verify Team ID and Channel ID
  2. Ensure user is a member of the team
  3. Check IDs don't have extra spaces

"Token expired"​

  1. Token auto-refreshes if refresh token exists
  2. Re-login if refresh fails
  3. Check tokenExpiresAt in database

Testing

Use Microsoft Graph Explorer to test API calls with your account before implementing in the workflow.