If you manage any Dynamics 365 Business Central clients where users sign in as Entra B2B guests (aka M365 Guest Users) you've probably hit this issue: the built-in email connectors don't work properly for guest users.
The use of guest accounts in BC is becoming more common due to organisations with a multi-tenancy strategy or where companies are looking for very clear separation of data access & governance. The expectation is simply that users email will just work in BC guest or not!
The Problem
Business Central (2026 wave 1) ships with three email connector options:
- Current User (built-in) - works great, but only for accounts native to the BC host tenant. If you're a guest user with a cross-tenant identity, BC can't send mail for you.
- M365 / Shared Mailbox - This is also dead-end for guest users, as they simply can't use or access other users or shared mailboxes in the tenancy where they are a guest.
- SMTP - sends messages using good old SMTP - which is as old as the internet itself! This is one way to deal with guest users, but can't meet a modern per-user approach.
The result: the scope for guest users to send email is quite narrow, only SMTP. A key requirement of modern connected system is clarity of identity when communicating with colleagues, customers or vendors. Being able to send as yourself is an important capability!
The Solution - Current User Email API
The extension implements the same "Current User" pattern that BC uses natively - but via the Microsoft Graph API, which works regardless of whether you're a guest or a member.
Here's how it works:
- Admin sets up once - create an Entra app registration with
Mail.Senddelegated permission, enter the details in BC. That's it for admin. - Each user consents once - open the "Connect Current User Email API" page, click a button, sign in with your work account in the popup, approve access. Done.
- Every email send works automatically - compose dialog, customer statements, scheduled reports, background jobs, ISV extensions. The connector resolves the correct Graph token for whoever is sending at that moment.
There is no per-user admin action required. No routing rules. No account-per-user management. One email account is registered with BC's email framework and set as the default. At send time, the connector looks up the current user's stored OAuth token and calls Graph as that user.
The User Experience
At present the users must access a consent page to confirm their connectin, In the next iteration I will aim to move closer to the 'current user' type experience so the users do not need to carry out this step and the necessary token for access to send email is managed from memory only and not stored at all. Currently the consent page shows a simple "Connect my Email" button. A popup opens, the user signs in with their normal work account, approves access, and the popup closes automatically. The page updates to show they're connected.
| 1 click to connect to the email API |
How It Works Under the Hood
For those who want to know what's going on technically:
- OAuth 2.0 Authorization Code + PKCE - the standard delegated flow. Each user authenticates as themselves and grants
Mail.Sendpermission to the app. - Token storage in IsolatedStorage - per-user (
DataScope::User), private to each user. No other user or background task running as a different identity can access it. - Silent token refresh - access tokens expire after about 60 minutes. The extension automatically refreshes them using the stored refresh token. Users don't need to re-consent unless they explicitly disconnect.
- Graph API
POST /v1.0/me/sendMail- the actual send./meresolves to the authenticated user's mailbox, whether they're a guest or a member. The email arrives from their real work address.
The architecture is simple: one fixed-GUID email account in BC's email framework, one row per user in a token tracking table, and the Graph call resolves the right identity at runtime via UserSecurityId().
In the next version we will move to
Built with AI, Reviewed by a Human
The code was written by GitHub Copilot (Claude), with me prompting and testing. It's important to review AI generated code, especially for security. Arend-Jan Kauffmann reviewed the app and identified several important improvements around token security, storage encryption, and BC platform best practices. That feedback is driving the next phase of development.
What's Next
The current release is a working proof of concept - Phase 1 and 2 are complete. Phase 3 is scoped and includes:
- Security hardening -
SecretTextparameters,[NonDebuggable]attributes, encrypted storage for the client secret, and removing unnecessary token persistence - System Application adoption - replacing raw
HttpClientwith BC'sRest Clientmodule and evaluating the built-inOAuth2codeunit - Multi-tenancy support - row-based setup for environments where guests come from multiple home tenancies that each need a separate Entra app registration
- Email attachment support - critical for BC where almost every email includes a file. The extension will handle inline attachments for small files and Graph upload sessions for files up to 30MB+
Try It / Contribute
The extension is open source and available on GitHub:
github.com/andywingate/D365BC-guest-email-api
It's a per-tenant extension (PTE) - you can deploy it to any BC SaaS environment. The QUICKSTART guide walks through the full setup: Entra app registration, BC configuration, and user consent.
If you're hitting the guest email problem in your BC environment, give it a try. If you have feedback, ideas, or want to contribute - open an issue or a PR on GitHub.
Resources
- GitHub - D365BC-guest-email-api
- Microsoft Learn - Set up email in Business Central
- Microsoft Graph - sendMail API
- AJ Kauffmann's RestClientOAuth library
What do you think?
Connect or follow me on LinkedIn to get all my updates Andrew Wingate | LinkedIn
