Microsoft Integration
ConsultFlow integrates deeply with Microsoft 365 services via Microsoft Graph API.
Supported Services
| Service | Capability | API |
|---|---|---|
| Azure AD | Authentication | OAuth 2.0 |
| Outlook | Send emails | Mail.Send |
| Outlook Calendar | Create events | Calendars.ReadWrite |
| Microsoft Teams | Post messages | ChannelMessage.Send |
Authentication
Azure AD OAuth 2.0
ConsultFlow uses delegated permissions, meaning actions are performed on behalf of the signed-in user.
Required Permissions:
| Permission | Type | Purpose |
|---|---|---|
| User.Read | Delegated | Read user profile |
| Mail.Send | Delegated | Send emails |
| Calendars.ReadWrite | Delegated | Create calendar events |
| ChannelMessage.Send | Delegated | Post to Teams |
| offline_access | Delegated | Refresh 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:
- Go to Graph Explorer
- Sign in with your Microsoft account
- Run:
GET https://graph.microsoft.com/v1.0/me/joinedTeams - Find your team ID
- Run:
GET https://graph.microsoft.com/v1.0/teams/{team-id}/channels - 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 Code | Description | Solution |
|---|---|---|
| 401 | Unauthorized | Re-authenticate, check token |
| 403 | Forbidden | Missing permissions |
| 404 | Not Found | Invalid ID (team, channel) |
| 429 | Throttled | Too 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"
- Check permissions in Azure Portal
- Grant admin consent if required
- Re-authenticate to get new token
"Resource not found"
- Verify Team ID and Channel ID
- Ensure user is a member of the team
- Check IDs don't have extra spaces
"Token expired"
- Token auto-refreshes if refresh token exists
- Re-login if refresh fails
- Check
tokenExpiresAtin database
Testing
Use Microsoft Graph Explorer to test API calls with your account before implementing in the workflow.